diff --git a/src/main/java/org/codehaus/plexus/interpolation/Interpolator.java b/src/main/java/org/codehaus/plexus/interpolation/Interpolator.java
index 7ed2c88..9581c7a 100644
--- a/src/main/java/org/codehaus/plexus/interpolation/Interpolator.java
+++ b/src/main/java/org/codehaus/plexus/interpolation/Interpolator.java
@@ -88,6 +88,62 @@ public interface Interpolator extends BasicInterpolator {
String interpolate(String input, String thisPrefixPattern, RecursionInterceptor recursionInterceptor)
throws InterpolationException;
+ /**
+ * Attempt to resolve all expressions in the given input string, using the
+ * provided lists of value sources and post processors. This method allows
+ * for efficient interpolation without the need to repeatedly add and remove
+ * value sources and post processors from the interpolator instance.
+ *
+ * This method triggers the use of a {@link SimpleRecursionInterceptor}
+ * instance for protection against expression cycles.
+ *
+ * return an empty String if input is null
+ *
+ * @param input The input string to interpolate
+ * @param valueSources The list of value sources to use for resolving expressions.
+ * If null or empty, no value sources will be used.
+ * @param postProcessors The list of post processors to apply after interpolation.
+ * If null or empty, no post processors will be used.
+ * @return interpolated string.
+ * @throws InterpolationException in case of an error.
+ * @since 1.29
+ */
+ default String interpolate(
+ String input, List valueSources, List postProcessors)
+ throws InterpolationException {
+ return interpolate(input, valueSources, postProcessors, new SimpleRecursionInterceptor());
+ }
+
+ /**
+ * Attempt to resolve all expressions in the given input string, using the
+ * provided lists of value sources and post processors. This method allows
+ * for efficient interpolation without the need to repeatedly add and remove
+ * value sources and post processors from the interpolator instance.
+ *
+ * The supplied recursion interceptor will provide protection from expression
+ * cycles, ensuring that the input can be resolved or an exception is thrown.
+ *
+ * return an empty String if input is null
+ *
+ * @param input The input string to interpolate
+ * @param valueSources The list of value sources to use for resolving expressions.
+ * If null or empty, no value sources will be used.
+ * @param postProcessors The list of post processors to apply after interpolation.
+ * If null or empty, no post processors will be used.
+ * @param recursionInterceptor Used to protect the interpolation process
+ * from expression cycles, and throw an
+ * exception if one is detected.
+ * @return interpolated string.
+ * @throws InterpolationException in case of an error.
+ * @since 1.29
+ */
+ String interpolate(
+ String input,
+ List valueSources,
+ List postProcessors,
+ RecursionInterceptor recursionInterceptor)
+ throws InterpolationException;
+
/**
* Return any feedback messages and errors that were generated - but
* suppressed - during the interpolation process. Since unresolvable
diff --git a/src/main/java/org/codehaus/plexus/interpolation/RegexBasedInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/RegexBasedInterpolator.java
index 0880987..f3d27d2 100644
--- a/src/main/java/org/codehaus/plexus/interpolation/RegexBasedInterpolator.java
+++ b/src/main/java/org/codehaus/plexus/interpolation/RegexBasedInterpolator.java
@@ -248,10 +248,39 @@ private String interpolate(
String expressionDelimiterEnd,
int realExprGroup)
throws InterpolationException {
+ return interpolate(
+ input,
+ recursionInterceptor,
+ expressionPattern,
+ expressionDelimiterStart,
+ expressionDelimiterEnd,
+ realExprGroup,
+ this.valueSources,
+ this.postProcessors);
+ }
+
+ private String interpolate(
+ String input,
+ RecursionInterceptor recursionInterceptor,
+ Pattern expressionPattern,
+ String expressionDelimiterStart,
+ String expressionDelimiterEnd,
+ int realExprGroup,
+ List valueSources,
+ List postProcessors)
+ throws InterpolationException {
if (input == null) {
// return empty String to prevent NPE too
return "";
}
+
+ // Use instance value sources if provided list is null or empty
+ List effectiveValueSources =
+ (valueSources != null && !valueSources.isEmpty()) ? valueSources : this.valueSources;
+ // Use instance post processors if provided list is null or empty
+ List effectivePostProcessors =
+ (postProcessors != null && !postProcessors.isEmpty()) ? postProcessors : this.postProcessors;
+
String result = input;
Matcher matcher = expressionPattern.matcher(result);
@@ -271,7 +300,7 @@ private String interpolate(
recursionInterceptor.expressionResolutionStarted(realExpr);
try {
Object value = existingAnswers.get(realExpr);
- for (ValueSource vs : valueSources) {
+ for (ValueSource vs : effectiveValueSources) {
if (value != null) break;
value = vs.getValue(realExpr, expressionDelimiterStart, expressionDelimiterEnd);
@@ -284,10 +313,12 @@ private String interpolate(
expressionPattern,
expressionDelimiterStart,
expressionDelimiterEnd,
- realExprGroup);
+ realExprGroup,
+ effectiveValueSources,
+ effectivePostProcessors);
- if (postProcessors != null && !postProcessors.isEmpty()) {
- for (InterpolationPostProcessor postProcessor : postProcessors) {
+ if (effectivePostProcessors != null && !effectivePostProcessors.isEmpty()) {
+ for (InterpolationPostProcessor postProcessor : effectivePostProcessors) {
Object newVal = postProcessor.execute(realExpr, value);
if (newVal != null) {
value = newVal;
@@ -373,7 +404,7 @@ public String interpolate(String input, String thisPrefixPattern) throws Interpo
* @param input The input string to interpolate
*/
public String interpolate(String input) throws InterpolationException {
- return interpolate(input, null, null);
+ return interpolate(input, (String) null, (RecursionInterceptor) null);
}
/**
@@ -393,6 +424,65 @@ public String interpolate(String input, RecursionInterceptor recursionIntercepto
return interpolate(input, null, recursionInterceptor);
}
+ /**
+ * {@inheritDoc}
+ */
+ public String interpolate(
+ String input,
+ List valueSources,
+ List postProcessors,
+ RecursionInterceptor recursionInterceptor)
+ throws InterpolationException {
+ if (input == null) {
+ // return empty String to prevent NPE too
+ return "";
+ }
+ if (recursionInterceptor == null) {
+ recursionInterceptor = new SimpleRecursionInterceptor();
+ }
+
+ String thisPrefixPattern = null;
+ int realExprGroup = 2;
+ Pattern expressionPattern;
+ final String expressionDelimiterStart;
+ final String expressionDelimiterEnd;
+ if (startRegex != null || endRegex != null) {
+ if (thisPrefixPattern == null) {
+ expressionPattern = getPattern(startRegex + endRegex);
+ realExprGroup = 1;
+ } else {
+ expressionPattern = getPattern(startRegex + thisPrefixPattern + endRegex);
+ }
+ expressionDelimiterStart = startRegex;
+ expressionDelimiterEnd = endRegex;
+
+ } else {
+ expressionDelimiterStart = "${";
+ expressionDelimiterEnd = "}";
+ if (thisPrefixPattern != null) {
+ expressionPattern = getPattern("\\$\\{(" + thisPrefixPattern + ")?(.+?)\\}");
+ } else {
+ expressionPattern = getPattern(DEFAULT_REGEXP);
+ realExprGroup = 1;
+ }
+ }
+ try {
+ return interpolate(
+ input,
+ recursionInterceptor,
+ expressionPattern,
+ expressionDelimiterStart,
+ expressionDelimiterEnd,
+ realExprGroup,
+ valueSources,
+ postProcessors);
+ } finally {
+ if (!cacheAnswers) {
+ clearAnswers();
+ }
+ }
+ }
+
public boolean isReusePatterns() {
return reusePatterns;
}
diff --git a/src/main/java/org/codehaus/plexus/interpolation/StringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/StringSearchInterpolator.java
index 28e2189..b099716 100644
--- a/src/main/java/org/codehaus/plexus/interpolation/StringSearchInterpolator.java
+++ b/src/main/java/org/codehaus/plexus/interpolation/StringSearchInterpolator.java
@@ -110,13 +110,48 @@ public String interpolate(String input, RecursionInterceptor recursionIntercepto
}
}
+ /**
+ * {@inheritDoc}
+ */
+ public String interpolate(
+ String input,
+ List valueSources,
+ List postProcessors,
+ RecursionInterceptor recursionInterceptor)
+ throws InterpolationException {
+ try {
+ return interpolate(input, recursionInterceptor, new HashSet(), valueSources, postProcessors);
+ } finally {
+ if (!cacheAnswers) {
+ existingAnswers.clear();
+ }
+ }
+ }
+
private String interpolate(String input, RecursionInterceptor recursionInterceptor, Set unresolvable)
throws InterpolationException {
+ return interpolate(input, recursionInterceptor, unresolvable, this.valueSources, this.postProcessors);
+ }
+
+ private String interpolate(
+ String input,
+ RecursionInterceptor recursionInterceptor,
+ Set unresolvable,
+ List valueSources,
+ List postProcessors)
+ throws InterpolationException {
if (input == null) {
// return empty String to prevent NPE too
return "";
}
+ // Use instance value sources if provided list is null or empty
+ List effectiveValueSources =
+ (valueSources != null && !valueSources.isEmpty()) ? valueSources : this.valueSources;
+ // Use instance post processors if provided list is null or empty
+ List effectivePostProcessors =
+ (postProcessors != null && !postProcessors.isEmpty()) ? postProcessors : this.postProcessors;
+
int startIdx;
int endIdx = -1;
if ((startIdx = input.indexOf(startExpr, endIdx + 1)) > -1) {
@@ -159,7 +194,7 @@ private String interpolate(String input, RecursionInterceptor recursionIntercept
Object value = getExistingAnswer(realExpr);
Object bestAnswer = null;
- for (ValueSource valueSource : valueSources) {
+ for (ValueSource valueSource : effectiveValueSources) {
if (value != null) {
break;
}
@@ -179,10 +214,15 @@ private String interpolate(String input, RecursionInterceptor recursionIntercept
}
if (value != null) {
- value = interpolate(String.valueOf(value), recursionInterceptor, unresolvable);
-
- if (postProcessors != null && !postProcessors.isEmpty()) {
- for (InterpolationPostProcessor postProcessor : postProcessors) {
+ value = interpolate(
+ String.valueOf(value),
+ recursionInterceptor,
+ unresolvable,
+ effectiveValueSources,
+ effectivePostProcessors);
+
+ if (effectivePostProcessors != null && !effectivePostProcessors.isEmpty()) {
+ for (InterpolationPostProcessor postProcessor : effectivePostProcessors) {
Object newVal = postProcessor.execute(realExpr, value);
if (newVal != null) {
value = newVal;
diff --git a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java
index c1a8541..9a3332b 100644
--- a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java
+++ b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java
@@ -133,12 +133,48 @@ public String interpolate(String input, RecursionInterceptor recursionIntercepto
}
}
+ /**
+ * {@inheritDoc}
+ */
+ public String interpolate(
+ String input,
+ List valueSources,
+ List postProcessors,
+ RecursionInterceptor recursionInterceptor)
+ throws InterpolationException {
+ try {
+ return interpolate(input, recursionInterceptor, new HashSet(), valueSources, postProcessors);
+ } finally {
+ if (!cacheAnswers) {
+ existingAnswers.clear();
+ }
+ }
+ }
+
private String interpolate(String input, RecursionInterceptor recursionInterceptor, Set unresolvable)
throws InterpolationException {
+ return interpolate(input, recursionInterceptor, unresolvable, this.valueSources, this.postProcessors);
+ }
+
+ private String interpolate(
+ String input,
+ RecursionInterceptor recursionInterceptor,
+ Set unresolvable,
+ List valueSources,
+ List postProcessors)
+ throws InterpolationException {
if (input == null) {
// return empty String to prevent NPE too
return "";
}
+
+ // Use instance value sources if provided list is null or empty
+ List effectiveValueSources =
+ (valueSources != null && !valueSources.isEmpty()) ? valueSources : this.valueSources;
+ // Use instance post processors if provided list is null or empty
+ List effectivePostProcessors =
+ (postProcessors != null && !postProcessors.isEmpty()) ? postProcessors : this.postProcessors;
+
StringBuilder result = new StringBuilder(input.length() * 2);
String lastResult = input;
@@ -198,7 +234,7 @@ private String interpolate(String input, RecursionInterceptor recursionIntercept
Object value = existingAnswers.get(realExpr);
Object bestAnswer = null;
- for (ValueSource vs : valueSources) {
+ for (ValueSource vs : effectiveValueSources) {
if (value != null) break;
value = vs.getValue(realExpr, startExpr, endExpr);
@@ -217,10 +253,15 @@ private String interpolate(String input, RecursionInterceptor recursionIntercept
}
if (value != null) {
- value = interpolate(String.valueOf(value), recursionInterceptor, unresolvable);
-
- if (postProcessors != null && !postProcessors.isEmpty()) {
- for (Object postProcessor1 : postProcessors) {
+ value = interpolate(
+ String.valueOf(value),
+ recursionInterceptor,
+ unresolvable,
+ effectiveValueSources,
+ effectivePostProcessors);
+
+ if (effectivePostProcessors != null && !effectivePostProcessors.isEmpty()) {
+ for (Object postProcessor1 : effectivePostProcessors) {
InterpolationPostProcessor postProcessor = (InterpolationPostProcessor) postProcessor1;
Object newVal = postProcessor.execute(realExpr, value);
if (newVal != null) {
diff --git a/src/test/java/org/codehaus/plexus/interpolation/InterpolateWithListsTest.java b/src/test/java/org/codehaus/plexus/interpolation/InterpolateWithListsTest.java
new file mode 100644
index 0000000..bf118d5
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/interpolation/InterpolateWithListsTest.java
@@ -0,0 +1,113 @@
+package org.codehaus.plexus.interpolation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class InterpolateWithListsTest {
+
+ @Test
+ public void testStringSearchInterpolatorWithLists() throws InterpolationException {
+ String src = "This is a ${test.label}.";
+ String result = "This is a test value.";
+
+ Properties p = new Properties();
+ p.setProperty("test.label", "test value");
+
+ List valueSources = new ArrayList<>();
+ valueSources.add(new PropertiesBasedValueSource(p));
+
+ List postProcessors = new ArrayList<>();
+
+ StringSearchInterpolator interpolator = new StringSearchInterpolator();
+ assertEquals(result, interpolator.interpolate(src, valueSources, postProcessors));
+ }
+
+ @Test
+ public void testRegexBasedInterpolatorWithLists() throws InterpolationException {
+ String src = "This is a ${test.label}.";
+ String result = "This is a test value.";
+
+ Properties p = new Properties();
+ p.setProperty("test.label", "test value");
+
+ List valueSources = new ArrayList<>();
+ valueSources.add(new PropertiesBasedValueSource(p));
+
+ List postProcessors = new ArrayList<>();
+
+ RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
+ assertEquals(result, interpolator.interpolate(src, valueSources, postProcessors));
+ }
+
+ @Test
+ public void testMultiDelimiterInterpolatorWithLists() throws InterpolationException {
+ String src = "This is a ${test.label}.";
+ String result = "This is a test value.";
+
+ Properties p = new Properties();
+ p.setProperty("test.label", "test value");
+
+ List valueSources = new ArrayList<>();
+ valueSources.add(new PropertiesBasedValueSource(p));
+
+ List postProcessors = new ArrayList<>();
+
+ org.codehaus.plexus.interpolation.multi.MultiDelimiterStringSearchInterpolator interpolator =
+ new org.codehaus.plexus.interpolation.multi.MultiDelimiterStringSearchInterpolator();
+ assertEquals(result, interpolator.interpolate(src, valueSources, postProcessors));
+ }
+
+ @Test
+ public void testInterpolatorWithPostProcessor() throws InterpolationException {
+ String src = "This is a ${test.label}.";
+ String result = "This is a PROCESSED.";
+
+ Properties p = new Properties();
+ p.setProperty("test.label", "test value");
+
+ List valueSources = new ArrayList<>();
+ valueSources.add(new PropertiesBasedValueSource(p));
+
+ List postProcessors = new ArrayList<>();
+ postProcessors.add(new InterpolationPostProcessor() {
+ @Override
+ public Object execute(String expression, Object value) {
+ if ("test.label".equals(expression)) {
+ return "PROCESSED";
+ }
+ return null;
+ }
+ });
+
+ StringSearchInterpolator interpolator = new StringSearchInterpolator();
+ assertEquals(result, interpolator.interpolate(src, valueSources, postProcessors));
+ }
+
+ @Test
+ public void testInstanceValueSourcesNotAffected() throws InterpolationException {
+ String src = "This is a ${test.label}.";
+
+ Properties p1 = new Properties();
+ p1.setProperty("test.label", "instance value");
+
+ Properties p2 = new Properties();
+ p2.setProperty("test.label", "list value");
+
+ StringSearchInterpolator interpolator = new StringSearchInterpolator();
+ interpolator.addValueSource(new PropertiesBasedValueSource(p1));
+
+ List valueSources = new ArrayList<>();
+ valueSources.add(new PropertiesBasedValueSource(p2));
+
+ // Should use the list value sources, not instance value sources
+ assertEquals("This is a list value.", interpolator.interpolate(src, valueSources, null));
+
+ // Should still use instance value sources with regular interpolate
+ assertEquals("This is a instance value.", interpolator.interpolate(src));
+ }
+}