You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ij...@apache.org on 2019/04/08 07:33:39 UTC
[nifi] 01/02: NIFI-5979 : enhanced ReplaceText processor with
"Number of Occurrences" and "Occurrence offset" configurations
This is an automated email from the ASF dual-hosted git repository.
ijokarumawak pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git
commit d222f14a9e9962fac1953180dfb4c0fe2bec524f
Author: pushpavanthar <pu...@gmail.com>
AuthorDate: Sun Mar 17 22:19:37 2019 +0530
NIFI-5979 : enhanced ReplaceText processor with "Number of Occurrences" and "Occurrence offset" configurations
Fixed indentation errors to pass checkstyle-checks
Added Evaluation Modes as per discussion in PR thread
Adding exclusions of test files on rat plugin
Added new property 'Line-by-Line Evaluation Mode' and refactored common code
This closes #3375.
Signed-off-by: Koji Kawamura <ij...@apache.org>
---
.../nifi-standard-processors/pom.xml | 8 +
.../nifi/processors/standard/ReplaceText.java | 369 +++++++++++---------
.../nifi/processors/standard/TestReplaceText.java | 374 +++++++++++++++++++++
.../LiteralReplaceExceptFirstLine.txt | 11 +
.../LiteralReplaceExceptLastLine.txt | 11 +
.../LiteralReplaceFirstLine.txt | 11 +
.../LiteralReplaceLastLine.txt | 11 +
.../ReplaceExceptFirstLine.txt | 11 +
.../ReplaceExceptLastLine.txt | 11 +
.../TestReplaceTextLineByLine/ReplaceFirstLine.txt | 11 +
.../TestReplaceTextLineByLine/ReplaceLastLine.txt | 11 +
11 files changed, 680 insertions(+), 159 deletions(-)
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
index f2d3d70..03e9bb4 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
@@ -429,6 +429,14 @@
<exclude>src/test/resources/TestReplaceTextLineByLine/testFile.txt</exclude>
<exclude>src/test/resources/TestReplaceTextLineByLine/AppendLineByLineTest.txt</exclude>
<exclude>src/test/resources/TestReplaceTextLineByLine/PrependLineByLineTest.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/ReplaceLastLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/ReplaceFirstLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/ReplaceExceptLastLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/ReplaceExceptFirstLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/LiteralReplaceLastLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/LiteralReplaceFirstLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptLastLine.txt</exclude>
+ <exclude>src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptFirstLine.txt</exclude>
<exclude>src/test/resources/TestReplaceTextWithMapping/color-fruit-backreference-mapping.txt</exclude>
<exclude>src/test/resources/TestReplaceTextWithMapping/color-fruit-blank-mapping.txt</exclude>
<exclude>src/test/resources/TestReplaceTextWithMapping/color-fruit-escaped-dollar-mapping.txt</exclude>
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ReplaceText.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ReplaceText.java
index c6aec0c..851770e 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ReplaceText.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ReplaceText.java
@@ -53,6 +53,7 @@ import org.apache.nifi.stream.io.StreamUtils;
import org.apache.nifi.stream.io.util.LineDemarcator;
import org.apache.nifi.util.StopWatch;
+import javax.annotation.Nullable;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -85,6 +86,11 @@ public class ReplaceText extends AbstractProcessor {
// Constants
public static final String LINE_BY_LINE = "Line-by-Line";
+ public static final String ALL = "All";
+ public static final String FIRST_LINE = "First-Line";
+ public static final String EXCEPT_FIRST_LINE = "Except-First-Line";
+ public static final String LAST_LINE = "Last-Line";
+ public static final String EXCEPT_LAST_LINE = "Except-Last-Line";
public static final String ENTIRE_TEXT = "Entire text";
public static final String prependValue = "Prepend";
public static final String appendValue = "Append";
@@ -99,10 +105,14 @@ public class ReplaceText extends AbstractProcessor {
// Properties PREPEND, APPEND, REGEX_REPLACE, LITERAL_REPLACE
static final AllowableValue PREPEND = new AllowableValue(prependValue, prependValue,
"Insert the Replacement Value at the beginning of the FlowFile or the beginning of each line (depending on the Evaluation Mode). For \"Line-by-Line\" Evaluation Mode, "
- + "the value will be prepended to each line. For \"Entire Text\" evaluation mode, the value will be prepended to the entire text.");
+ + "the value will be prepended to each line. Similarly, for \"First-Line\", \"Last-Line\", \"Except-Last-Line\" and \"Except-First-Line\" Evaluation Modes,"
+ + "the value will be prepended to header alone, footer alone, all lines except header and all lines except footer respectively. For \"Entire Text\" evaluation mode,"
+ + "the value will be prepended to the entire text.");
static final AllowableValue APPEND = new AllowableValue(appendValue, appendValue,
"Insert the Replacement Value at the end of the FlowFile or the end of each line (depending on the Evaluation Mode). For \"Line-by-Line\" Evaluation Mode, "
- + "the value will be appended to each line. For \"Entire Text\" evaluation mode, the value will be appended to the entire text.");
+ + "the value will be appended to each line. Similarly, for \"First-Line\", \"Last-Line\", \"Except-Last-Line\" and \"Except-First-Line\" Evaluation Modes,"
+ + "the value will be appended to header alone, footer alone, all lines except header and all lines except footer respectively. For \"Entire Text\" evaluation mode,"
+ + "the value will be appended to the entire text.");
static final AllowableValue LITERAL_REPLACE = new AllowableValue(literalReplaceValue, literalReplaceValue,
"Search for all instances of the Search Value and replace the matches with the Replacement Value.");
static final AllowableValue REGEX_REPLACE = new AllowableValue(regexReplaceValue, regexReplaceValue,
@@ -161,13 +171,22 @@ public class ReplaceText extends AbstractProcessor {
.build();
public static final PropertyDescriptor EVALUATION_MODE = new PropertyDescriptor.Builder()
.name("Evaluation Mode")
- .description("Run the 'Replacement Strategy' against each line separately (Line-by-Line) or buffer the entire file into memory (Entire Text) "
- + "and run against that.")
+ .description("Run the 'Replacement Strategy' against each line separately (Line-by-Line) or buffer the entire file "
+ + "into memory (Entire Text) and run against that.")
.allowableValues(LINE_BY_LINE, ENTIRE_TEXT)
.defaultValue(ENTIRE_TEXT)
.required(true)
.build();
+ public static final PropertyDescriptor LINE_BY_LINE_EVALUATION_MODE = new PropertyDescriptor.Builder()
+ .name("Line-by-Line Evaluation Mode")
+ .description("Run the 'Replacement Strategy' against each line separately (Line-by-Line) for all lines in the FlowFile, First Line (Header) alone, "
+ + "Last Line (Footer) alone, Except the First Line (Header) or Except the Last Line (Footer).")
+ .allowableValues(ALL, FIRST_LINE, LAST_LINE, EXCEPT_FIRST_LINE, EXCEPT_LAST_LINE)
+ .defaultValue(ALL)
+ .required(false)
+ .build();
+
// Relationships
public static final Relationship REL_SUCCESS = new Relationship.Builder()
.name("success")
@@ -191,6 +210,7 @@ public class ReplaceText extends AbstractProcessor {
properties.add(MAX_BUFFER_SIZE);
properties.add(REPLACEMENT_STRATEGY);
properties.add(EVALUATION_MODE);
+ properties.add(LINE_BY_LINE_EVALUATION_MODE);
this.properties = Collections.unmodifiableList(properties);
final Set<Relationship> relationships = new HashSet<>();
@@ -351,7 +371,7 @@ public class ReplaceText extends AbstractProcessor {
return value;
}
- private static class AlwaysReplace implements ReplacementStrategyExecutor {
+ private class AlwaysReplace implements ReplacementStrategyExecutor {
@Override
public FlowFile replace(FlowFile flowFile, final ProcessSession session, final ProcessContext context, final String evaluateMode, final Charset charset, final int maxBufferSize) {
@@ -366,46 +386,40 @@ public class ReplaceText extends AbstractProcessor {
}
});
} else {
- flowFile = session.write(flowFile, new StreamCallback() {
- @Override
- public void process(final InputStream in, final OutputStream out) throws IOException {
- try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, 8192);
- final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
-
- String line;
- while ((line = demarcator.nextLine()) != null) {
- // We need to determine what line ending was used and use that after our replacement value.
- lineEndingBuilder.setLength(0);
- for (int i = line.length() - 1; i >= 0; i--) {
- final char c = line.charAt(i);
- if (c == '\r' || c == '\n') {
- lineEndingBuilder.append(c);
- } else {
- break;
- }
- }
-
- bw.write(replacementValue);
-
- // Preserve original line endings. Reverse string because we iterated over original line ending in reverse order, appending to builder.
- // So if builder has multiple characters, they are now reversed from the original string's ordering.
- bw.write(lineEndingBuilder.reverse().toString());
- }
- }
- }
- });
+ flowFile = session.write(flowFile, new StreamReplaceCallback(this, charset, maxBufferSize, context, flowFile, null));
}
return flowFile;
}
+ public void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, @Nullable Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException {
+ final String replacementValue = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile).getValue();
+ final StringBuilder lineEndingBuilder = new StringBuilder(2);
+ // We need to determine what line ending was used and use that after our replacement value.
+ lineEndingBuilder.setLength(0);
+ for (int i = oneLine.length() - 1; i >= 0; i--) {
+ final char c = oneLine.charAt(i);
+ if (c == '\r' || c == '\n') {
+ lineEndingBuilder.append(c);
+ } else {
+ break;
+ }
+ }
+
+ bw.write(replacementValue);
+
+ // Preserve original line endings. Reverse string because we iterated over original line ending in reverse order, appending to builder.
+ // So if builder has multiple characters, they are now reversed from the original string's ordering.
+ bw.write(lineEndingBuilder.reverse().toString());
+ }
+
@Override
public boolean isAllDataBufferedForEntireText() {
return false;
}
}
- private static class PrependReplace implements ReplacementStrategyExecutor {
+ private class PrependReplace implements ReplacementStrategyExecutor {
@Override
public FlowFile replace(FlowFile flowFile, final ProcessSession session, final ProcessContext context, final String evaluateMode, final Charset charset, final int maxBufferSize) {
final String replacementValue = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile).getValue();
@@ -419,20 +433,7 @@ public class ReplaceText extends AbstractProcessor {
}
});
} else {
- flowFile = session.write(flowFile, new StreamCallback() {
- @Override
- public void process(final InputStream in, final OutputStream out) throws IOException {
- try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, 8192);
- final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
-
- String oneLine;
- while (null != (oneLine = demarcator.nextLine())) {
- final String updatedValue = replacementValue.concat(oneLine);
- bw.write(updatedValue);
- }
- }
- }
- });
+ flowFile = session.write(flowFile, new StreamReplaceCallback(this, charset, maxBufferSize, context, flowFile, null));
}
return flowFile;
}
@@ -441,9 +442,15 @@ public class ReplaceText extends AbstractProcessor {
public boolean isAllDataBufferedForEntireText() {
return false;
}
+
+ @Override
+ public void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, @Nullable Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException {
+ final String replacementValue = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile).getValue();
+ bw.write(replacementValue.concat(oneLine));
+ }
}
- private static class AppendReplace implements ReplacementStrategyExecutor {
+ private class AppendReplace implements ReplacementStrategyExecutor {
@Override
public FlowFile replace(FlowFile flowFile, final ProcessSession session, final ProcessContext context, final String evaluateMode, final Charset charset, final int maxBufferSize) {
@@ -458,45 +465,38 @@ public class ReplaceText extends AbstractProcessor {
}
});
} else {
- flowFile = session.write(flowFile, new StreamCallback() {
- @Override
- public void process(final InputStream in, final OutputStream out) throws IOException {
- try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, 8192);
- final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
-
- String oneLine;
- while (null != (oneLine = demarcator.nextLine())) {
- // we need to find the first carriage return or new-line so that we can append the new value
- // before the line separate. However, we don't want to do this using a regular expression due
- // to performance concerns. So we will find the first occurrence of either \r or \n and use
- // that to insert the replacement value.
- boolean foundNewLine = false;
- for (int i = 0; i < oneLine.length(); i++) {
- final char c = oneLine.charAt(i);
- if (foundNewLine) {
- bw.write(c);
- continue;
- }
-
- if (c == '\r' || c == '\n') {
- bw.write(replacementValue);
- foundNewLine = true;
- }
-
- bw.write(c);
- }
-
- if (!foundNewLine) {
- bw.write(replacementValue);
- }
- }
- }
- }
- });
+ flowFile = session.write(flowFile, new StreamReplaceCallback(this, charset, maxBufferSize, context, flowFile, null));
}
return flowFile;
}
+ public void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, @Nullable Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException {
+ String replacementValue = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile).getValue();
+ // we need to find the first carriage return or new-line so that we can append the new value
+ // before the line separate. However, we don't want to do this using a regular expression due
+ // to performance concerns. So we will find the first occurrence of either \r or \n and use
+ // that to insert the replacement value.
+ boolean foundNewLine = false;
+ for (int i = 0; i < oneLine.length(); i++) {
+ final char c = oneLine.charAt(i);
+ if (foundNewLine) {
+ bw.write(c);
+ continue;
+ }
+
+ if (c == '\r' || c == '\n') {
+ bw.write(replacementValue);
+ foundNewLine = true;
+ }
+
+ bw.write(c);
+ }
+
+ if (!foundNewLine) {
+ bw.write(replacementValue);
+ }
+ }
+
@Override
public boolean isAllDataBufferedForEntireText() {
return false;
@@ -504,13 +504,13 @@ public class ReplaceText extends AbstractProcessor {
}
- private static class RegexReplace implements ReplacementStrategyExecutor {
+ private class RegexReplace implements ReplacementStrategyExecutor {
private final byte[] buffer;
private final int numCapturingGroups;
private final Map<String, String> additionalAttrs;
// back references are not supported in the evaluated expression
- private static final AttributeValueDecorator escapeBackRefDecorator = new AttributeValueDecorator() {
+ private final AttributeValueDecorator escapeBackRefDecorator = new AttributeValueDecorator() {
@Override
public String decorate(final String attributeValue) {
// when we encounter a '$[0-9+]' replace it with '\$[0-9+]'
@@ -580,58 +580,46 @@ public class ReplaceText extends AbstractProcessor {
}
} else {
- updatedFlowFile = session.write(flowFile, new StreamCallback() {
- @Override
- public void process(final InputStream in, final OutputStream out) throws IOException {
- try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, 8192);
- final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
-
- String oneLine;
+ updatedFlowFile = session.write(flowFile, new StreamReplaceCallback(this, charset, maxBufferSize, context, flowFile, searchPattern));
+ }
- final StringBuffer sb = new StringBuffer();
- Matcher matcher = null;
+ return updatedFlowFile;
+ }
- while (null != (oneLine = demarcator.nextLine())) {
- additionalAttrs.clear();
- if (matcher == null) {
- matcher = searchPattern.matcher(oneLine);
- } else {
- matcher.reset(oneLine);
- }
+ public void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException {
+ additionalAttrs.clear();
+ if (matcher == null) {
+ matcher = searchPattern.matcher(oneLine);
+ } else {
+ matcher.reset(oneLine);
+ }
- int matches = 0;
- sb.setLength(0);
+ int matches = 0;
+ StringBuffer sb = new StringBuffer();
- while (matcher.find()) {
- matches++;
+ while (matcher.find()) {
+ matches++;
- for (int i=0; i <= matcher.groupCount(); i++) {
- additionalAttrs.put("$" + i, matcher.group(i));
- }
+ for (int i=0; i <= matcher.groupCount(); i++) {
+ additionalAttrs.put("$" + i, matcher.group(i));
+ }
- String replacement = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile, additionalAttrs, escapeBackRefDecorator).getValue();
- replacement = escapeLiteralBackReferences(replacement, numCapturingGroups);
- String replacementFinal = normalizeReplacementString(replacement);
+ String replacement = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile, additionalAttrs, escapeBackRefDecorator).getValue();
+ replacement = escapeLiteralBackReferences(replacement, numCapturingGroups);
+ String replacementFinal = normalizeReplacementString(replacement);
- matcher.appendReplacement(sb, replacementFinal);
- }
+ matcher.appendReplacement(sb, replacementFinal);
+ }
- if (matches > 0) {
- matcher.appendTail(sb);
+ if (matches > 0) {
+ matcher.appendTail(sb);
- final String updatedValue = sb.toString();
- bw.write(updatedValue);
- } else {
- // No match. Just write out the line as it was.
- bw.write(oneLine);
- }
- }
- }
- }
- });
+ final String updatedValue = sb.toString();
+ bw.write(updatedValue);
+ } else {
+ // No match. Just write out the line as it was.
+ bw.write(oneLine);
}
-
- return updatedFlowFile;
}
@Override
@@ -640,7 +628,7 @@ public class ReplaceText extends AbstractProcessor {
}
}
- private static class LiteralReplace implements ReplacementStrategyExecutor {
+ private class LiteralReplace implements ReplacementStrategyExecutor {
private final byte[] buffer;
public LiteralReplace(final byte[] buffer) {
@@ -667,42 +655,34 @@ public class ReplaceText extends AbstractProcessor {
}
});
} else {
- final int initialBufferSize = (int) Math.min(flowFile.getSize(), 8192);
final Pattern searchPattern = Pattern.compile(searchValue, Pattern.LITERAL);
- flowFile = session.write(flowFile, new StreamCallback() {
- @Override
- public void process(final InputStream in, final OutputStream out) throws IOException {
- try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, initialBufferSize);
- final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
-
- String oneLine;
- while (null != (oneLine = demarcator.nextLine())) {
- int matches = 0;
- int lastEnd = 0;
-
- final Matcher matcher = searchPattern.matcher(oneLine);
- while (matcher.find()) {
- bw.write(oneLine, lastEnd, matcher.start() - lastEnd);
- bw.write(replacementValue);
- matches++;
-
- lastEnd = matcher.end();
- }
-
- if (matches > 0) {
- bw.write(oneLine, lastEnd, oneLine.length() - lastEnd);
- } else {
- bw.write(oneLine);
- }
- }
- }
- }
- });
+ flowFile = session.write(flowFile, new StreamReplaceCallback(this, charset, maxBufferSize, context, flowFile, searchPattern));
}
return flowFile;
}
+ public void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, @Nullable Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException {
+ String replacementValue = context.getProperty(REPLACEMENT_VALUE).evaluateAttributeExpressions(flowFile).getValue();
+ int matches = 0;
+ int lastEnd = 0;
+
+
+ while (matcher.find()) {
+ bw.write(oneLine, lastEnd, matcher.start() - lastEnd);
+ bw.write(replacementValue);
+ matches++;
+
+ lastEnd = matcher.end();
+ }
+
+ if (matches > 0) {
+ bw.write(oneLine, lastEnd, oneLine.length() - lastEnd);
+ } else {
+ bw.write(oneLine);
+ }
+ }
+
@Override
public boolean isAllDataBufferedForEntireText() {
return true;
@@ -726,5 +706,76 @@ public class ReplaceText extends AbstractProcessor {
FlowFile replace(FlowFile flowFile, ProcessSession session, ProcessContext context, String evaluateMode, Charset charset, int maxBufferSize);
boolean isAllDataBufferedForEntireText();
+
+ void replaceInLine(BufferedWriter bw, String oneLine, @Nullable Matcher matcher, @Nullable Pattern searchPattern, ProcessContext context, FlowFile flowFile) throws IOException ;
+ }
+
+
+ private class StreamReplaceCallback implements StreamCallback {
+ private final Charset charset;
+ private final int maxBufferSize;
+ private final ProcessContext context;
+ private final FlowFile flowFile;
+ private final ReplacementStrategyExecutor replacementStrategyExecutor;
+ private final Pattern searchPattern;
+
+ public StreamReplaceCallback(ReplacementStrategyExecutor replacementStrategyExecutor,
+ Charset charset,
+ int maxBufferSize,
+ ProcessContext context,
+ FlowFile flowFile,
+ @Nullable Pattern searchPattern) {
+ this.replacementStrategyExecutor = replacementStrategyExecutor;
+ this.charset = charset;
+ this.maxBufferSize = maxBufferSize;
+ this.context = context;
+ this.flowFile = flowFile;
+ this.searchPattern = searchPattern;
+ }
+
+ @Override
+ public void process(final InputStream in, final OutputStream out) throws IOException {
+ final String lineByLineEvaluationMode = context.getProperty(LINE_BY_LINE_EVALUATION_MODE).getValue();
+ try (final LineDemarcator demarcator = new LineDemarcator(in, charset, maxBufferSize, 8192);
+ final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, charset))) {
+
+ String precedingLine = demarcator.nextLine();
+ String succeedingLine;
+ Matcher matcher = null;
+
+ boolean firstLine = true;
+
+ while (null != (succeedingLine = demarcator.nextLine())) {
+ matcher = null != searchPattern ? searchPattern.matcher(precedingLine) : null;
+ if(firstLine && lineByLineEvaluationMode.equalsIgnoreCase(FIRST_LINE)){
+ replacementStrategyExecutor.replaceInLine(bw, precedingLine, matcher, searchPattern, context, flowFile);
+ firstLine = false;
+ } else if(firstLine && lineByLineEvaluationMode.equalsIgnoreCase(EXCEPT_FIRST_LINE)) {
+ firstLine = false;
+ bw.write(precedingLine);
+ } else if(lineByLineEvaluationMode.equalsIgnoreCase(LINE_BY_LINE)
+ || lineByLineEvaluationMode.equalsIgnoreCase(EXCEPT_LAST_LINE)
+ || lineByLineEvaluationMode.equalsIgnoreCase(ALL)
+ || (!firstLine && lineByLineEvaluationMode.equalsIgnoreCase(EXCEPT_FIRST_LINE))) {
+ replacementStrategyExecutor.replaceInLine(bw, precedingLine, matcher, searchPattern, context, flowFile);
+ } else {
+ bw.write(precedingLine);
+ }
+ precedingLine = succeedingLine;
+ }
+
+ // 0 byte empty FlowFIles are left untouched
+ if(null != precedingLine) {
+ if (lineByLineEvaluationMode.equalsIgnoreCase(EXCEPT_LAST_LINE)
+ || (!firstLine && lineByLineEvaluationMode.equalsIgnoreCase(FIRST_LINE))
+ || (firstLine && lineByLineEvaluationMode.equalsIgnoreCase(EXCEPT_FIRST_LINE))) {
+ bw.write(precedingLine);
+ } else {
+ matcher = null != searchPattern ? searchPattern.matcher(precedingLine) : null;
+ replacementStrategyExecutor.replaceInLine(bw, precedingLine, matcher, searchPattern, context, flowFile);
+ }
+ }
+ }
+ }
}
}
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestReplaceText.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestReplaceText.java
index eb89221..b3200a8 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestReplaceText.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestReplaceText.java
@@ -167,6 +167,71 @@ public class TestReplaceText {
}
@Test
+ public void testPrependFirstLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "_");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.PREPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.FIRST_LINE);
+
+ runner.enqueue("hello\nthere\nmadam".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("_hello\nthere\nmadam".getBytes("UTF-8"));
+ }
+
+ @Test
+ public void testPrependLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "_");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.PREPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.LAST_LINE);
+
+ runner.enqueue("hello\nthere\nmadam".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\nthere\n_madam".getBytes("UTF-8"));
+ }
+
+ @Test
+ public void testPrependExceptFirstLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "_");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.PREPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_FIRST_LINE);
+
+ runner.enqueue("hello\nthere\nmadam".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\n_there\n_madam".getBytes("UTF-8"));
+ }
+
+
+ @Test
+ public void testPrependExceptLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "_");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.PREPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_LAST_LINE);
+
+ runner.enqueue("hello\nthere\nmadam".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("_hello\n_there\nmadam".getBytes("UTF-8"));
+ }
+
+ @Test
public void testAppendSimple() throws IOException {
final TestRunner runner = getRunner();
runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "TEST");
@@ -195,6 +260,71 @@ public class TestReplaceText {
out.assertContentEquals("hello!\rthere!\rsir!");
}
+
+ @Test
+ public void testAppendFirstLineWithCarriageReturn() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.FIRST_LINE);
+
+ runner.enqueue("hello\rthere\rsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello!\rthere\rsir");
+ }
+
+ @Test
+ public void testAppendExceptFirstLineWithCarriageReturn() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_FIRST_LINE);
+
+ runner.enqueue("hello\rthere\rsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\rthere!\rsir!");
+ }
+
+ @Test
+ public void testAppendLastLineWithCarriageReturn() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.LAST_LINE);
+
+ runner.enqueue("hello\rthere\rsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\rthere\rsir!");
+ }
+
+ @Test
+ public void testAppendExceptLastLineWithCarriageReturn() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_LAST_LINE);
+
+ runner.enqueue("hello\rthere\rsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello!\rthere!\rsir");
+ }
+
@Test
public void testAppendWithNewLine() throws IOException {
final TestRunner runner = getRunner();
@@ -226,6 +356,74 @@ public class TestReplaceText {
}
@Test
+ public void testAppendFirstLineWithCarriageReturnNewLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.FIRST_LINE);
+
+ runner.enqueue("hello\r\nthere\r\nsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello!\r\nthere\r\nsir");
+ }
+
+
+ @Test
+ public void testAppendLastLineWithCarriageReturnNewLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.LAST_LINE);
+
+ runner.enqueue("hello\r\nthere\r\nsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\r\nthere\r\nsir!");
+ }
+
+
+ @Test
+ public void testAppendExceptFistLineWithCarriageReturnNewLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_FIRST_LINE);
+
+ runner.enqueue("hello\r\nthere\r\nsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello\r\nthere!\r\nsir!");
+ }
+
+
+ @Test
+ public void testAppendExceptLastLineWithCarriageReturnNewLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "!");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.APPEND);
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_LAST_LINE);
+
+
+ runner.enqueue("hello\r\nthere\r\nsir".getBytes());
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals("hello!\r\nthere!\r\nsir");
+ }
+
+ @Test
public void testLiteralSimple() throws IOException {
final TestRunner runner = getRunner();
runner.setProperty(ReplaceText.SEARCH_VALUE, "ell");
@@ -648,6 +846,24 @@ public class TestReplaceText {
out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/food.txt")));
}
+
+ @Test
+ public void testZeroByteContentFileLineByLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "odo");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "ood");
+
+ final File zeroByteFile = File.createTempFile("zeroByte", ".txt");
+ runner.enqueue(translateNewLines(zeroByteFile.getPath()));
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(zeroByteFile.getPath()));
+ }
+
+
@Test
public void testPrependSimpleLineByLine() throws IOException {
final TestRunner runner = getRunner();
@@ -847,6 +1063,164 @@ public class TestReplaceText {
out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/Blu$2e_clu$2e.txt")));
}
+
+ @Test
+ public void testBackReferenceWithTooLargeOfIndexIsEscapedFirstLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.FIRST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "(H)");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "$1$2");
+
+ final Map<String, String> attributes = new HashMap<>();
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")), attributes);
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/ReplaceFirstLine.txt")));
+ }
+
+
+ @Test
+ public void testBackReferenceWithTooLargeOfIndexIsEscapedLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.LAST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "(O)");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "$1$2");
+
+ final Map<String, String> attributes = new HashMap<>();
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")), attributes);
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/ReplaceLastLine.txt")));
+ }
+
+
+
+ @Test
+ public void testBackReferenceWithTooLargeOfIndexIsEscapedExceptFirstLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_FIRST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "(H)");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "$1$2");
+
+ final Map<String, String> attributes = new HashMap<>();
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")), attributes);
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/ReplaceExceptFirstLine.txt")));
+ }
+
+
+
+
+ @Test
+ public void testBackReferenceWithTooLargeOfIndexIsEscapedExceptLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_LAST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "(O)");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "$1$2");
+
+ final Map<String, String> attributes = new HashMap<>();
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")), attributes);
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/ReplaceExceptLastLine.txt")));
+ }
+
+
+
+ @Test
+ public void testLiteralBackReferenceFistLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.FIRST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "H");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "[$1]");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.LITERAL_REPLACE);
+
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")));
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/LiteralReplaceFirstLine.txt")));
+ }
+
+
+
+
+ @Test
+ public void testLiteralBackReferenceExceptFirstLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_FIRST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "H");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "[$1]");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.LITERAL_REPLACE);
+
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")));
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptFirstLine.txt")));
+ }
+
+
+ @Test
+ public void testLiteralBackReferenceLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.LAST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "O");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "[$1]");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.LITERAL_REPLACE);
+
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")));
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/LiteralReplaceLastLine.txt")));
+ }
+
+
+ @Test
+ public void testLiteralBackReferenceExceptLastLine() throws IOException {
+ final TestRunner runner = getRunner();
+ runner.setProperty(ReplaceText.EVALUATION_MODE, ReplaceText.LINE_BY_LINE);
+ runner.setProperty(ReplaceText.LINE_BY_LINE_EVALUATION_MODE, ReplaceText.EXCEPT_LAST_LINE);
+ runner.setProperty(ReplaceText.SEARCH_VALUE, "O");
+ runner.setProperty(ReplaceText.REPLACEMENT_VALUE, "[$1]");
+ runner.setProperty(ReplaceText.REPLACEMENT_STRATEGY, ReplaceText.LITERAL_REPLACE);
+
+ runner.enqueue(translateNewLines(Paths.get("src/test/resources/TestReplaceTextLineByLine/testFile.txt")));
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ReplaceText.REL_SUCCESS, 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship(ReplaceText.REL_SUCCESS).get(0);
+ out.assertContentEquals(translateNewLines(new File("src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptLastLine.txt")));
+ }
+
@Test
public void testBackReferenceWithInvalidReferenceIsEscapedLineByLine() throws IOException {
final TestRunner runner = getRunner();
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptFirstLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptFirstLine.txt
new file mode 100644
index 0000000..b745d4a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptFirstLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley [$1]uey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley [$1]uey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley [$1]uey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptLastLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptLastLine.txt
new file mode 100644
index 0000000..6627585
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceExceptLastLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo D[$1]D[$1] cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo D[$1]D[$1] cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo D[$1]D[$1] cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceFirstLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceFirstLine.txt
new file mode 100644
index 0000000..bc775d8
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceFirstLine.txt
@@ -0,0 +1,11 @@
+<<<[$1]EADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceLastLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceLastLine.txt
new file mode 100644
index 0000000..9081f4f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/LiteralReplaceLastLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<F[$1][$1]TER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptFirstLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptFirstLine.txt
new file mode 100644
index 0000000..b236452
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptFirstLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley H$2uey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley H$2uey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley H$2uey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptLastLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptLastLine.txt
new file mode 100644
index 0000000..93f4df6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceExceptLastLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo DO$2DO$2 cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DO$2DO$2 cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DO$2DO$2 cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceFirstLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceFirstLine.txt
new file mode 100644
index 0000000..6cd3d66
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceFirstLine.txt
@@ -0,0 +1,11 @@
+<<<H$2EADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<FOOTER>>>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceLastLine.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceLastLine.txt
new file mode 100644
index 0000000..d17e7ed
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestReplaceTextLineByLine/ReplaceLastLine.txt
@@ -0,0 +1,11 @@
+<<<HEADER>>>
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+Fodo DODO cujo Pojo
+Blue Dew clue hew
+Grampa Riley Huey
+<<<FO$2O$2TER>>>
\ No newline at end of file