mirror of https://github.com/apache/maven.git
[MNG-8344] Support multiple operators in variable expansion (#1832)
This commit is contained in:
parent
dc6d48ce00
commit
54df597018
|
@ -268,66 +268,8 @@ public class DefaultInterpolator implements Interpolator {
|
||||||
String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
|
String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
|
||||||
String org = variable;
|
String org = variable;
|
||||||
|
|
||||||
// Strip expansion modifiers
|
String substValue = processSubstitution(
|
||||||
int idx1 = variable.lastIndexOf(":-");
|
variable, org, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
||||||
int idx2 = variable.lastIndexOf(":+");
|
|
||||||
int idx = idx1 >= 0 ? idx2 >= 0 ? Math.min(idx1, idx2) : idx1 : idx2;
|
|
||||||
String op = null;
|
|
||||||
if (idx >= 0) {
|
|
||||||
op = variable.substring(idx);
|
|
||||||
variable = variable.substring(0, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that this is not a recursive variable reference.
|
|
||||||
if (!cycleMap.add(variable)) {
|
|
||||||
throw new InterpolatorException("recursive variable reference: " + variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
String substValue = null;
|
|
||||||
// Get the value of the deepest nested variable placeholder.
|
|
||||||
// Try to configuration properties first.
|
|
||||||
if (configProps != null) {
|
|
||||||
substValue = configProps.get(variable);
|
|
||||||
}
|
|
||||||
if (substValue == null) {
|
|
||||||
if (!variable.isEmpty()) {
|
|
||||||
if (callback != null) {
|
|
||||||
String s1 = callback.apply(variable);
|
|
||||||
String s2 = doSubstVars(
|
|
||||||
s1, variable, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
|
||||||
substValue = postprocessor != null ? postprocessor.apply(variable, s2) : s2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op != null) {
|
|
||||||
if (op.startsWith(":-")) {
|
|
||||||
if (substValue == null || substValue.isEmpty()) {
|
|
||||||
substValue = op.substring(":-".length());
|
|
||||||
}
|
|
||||||
} else if (op.startsWith(":+")) {
|
|
||||||
if (substValue != null && !substValue.isEmpty()) {
|
|
||||||
substValue = op.substring(":+".length());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new InterpolatorException("Bad substitution: ${" + org + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substValue == null) {
|
|
||||||
if (defaultsToEmptyString) {
|
|
||||||
substValue = "";
|
|
||||||
} else {
|
|
||||||
// alters the original token to avoid infinite recursion
|
|
||||||
// altered tokens are reverted in unescape()
|
|
||||||
substValue = MARKER + "{" + variable + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the found variable from the cycle map, since
|
|
||||||
// it may appear more than once in the value and we don't
|
|
||||||
// want such situations to appear as a recursive reference.
|
|
||||||
cycleMap.remove(variable);
|
|
||||||
|
|
||||||
// Append the leading characters, the substituted value of
|
// Append the leading characters, the substituted value of
|
||||||
// the variable, and the trailing characters to get the new
|
// the variable, and the trailing characters to get the new
|
||||||
|
@ -344,6 +286,111 @@ public class DefaultInterpolator implements Interpolator {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String processSubstitution(
|
||||||
|
String variable,
|
||||||
|
String org,
|
||||||
|
Set<String> cycleMap,
|
||||||
|
Map<String, String> configProps,
|
||||||
|
Function<String, String> callback,
|
||||||
|
BiFunction<String, String, String> postprocessor,
|
||||||
|
boolean defaultsToEmptyString) {
|
||||||
|
|
||||||
|
// Process chained operators from left to right
|
||||||
|
int startIdx = 0;
|
||||||
|
String currentVar = variable;
|
||||||
|
String substValue = null;
|
||||||
|
|
||||||
|
while (startIdx < variable.length()) {
|
||||||
|
int idx1 = variable.indexOf(":-", startIdx);
|
||||||
|
int idx2 = variable.indexOf(":+", startIdx);
|
||||||
|
int idx = idx1 >= 0 ? idx2 >= 0 ? Math.min(idx1, idx2) : idx1 : idx2;
|
||||||
|
|
||||||
|
if (idx < 0) {
|
||||||
|
// No more operators, process the final variable
|
||||||
|
if (substValue == null) {
|
||||||
|
currentVar = variable.substring(startIdx);
|
||||||
|
substValue = resolveVariable(
|
||||||
|
currentVar, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current variable part before the operator
|
||||||
|
String varPart = variable.substring(startIdx, idx);
|
||||||
|
if (substValue == null) {
|
||||||
|
substValue =
|
||||||
|
resolveVariable(varPart, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the end of the current operator's value
|
||||||
|
int nextIdx1 = variable.indexOf(":-", idx + 2);
|
||||||
|
int nextIdx2 = variable.indexOf(":+", idx + 2);
|
||||||
|
int nextIdx = nextIdx1 >= 0 ? nextIdx2 >= 0 ? Math.min(nextIdx1, nextIdx2) : nextIdx1 : nextIdx2;
|
||||||
|
|
||||||
|
String op = variable.substring(idx, idx + 2);
|
||||||
|
String opValue = variable.substring(idx + 2, nextIdx >= 0 ? nextIdx : variable.length());
|
||||||
|
|
||||||
|
// Process the operator value through substitution if it contains variables
|
||||||
|
String processedOpValue =
|
||||||
|
doSubstVars(opValue, org, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
||||||
|
|
||||||
|
// Apply the operator
|
||||||
|
if (":+".equals(op)) {
|
||||||
|
if (substValue != null && !substValue.isEmpty()) {
|
||||||
|
substValue = processedOpValue;
|
||||||
|
}
|
||||||
|
} else if (":-".equals(op)) {
|
||||||
|
if (substValue == null || substValue.isEmpty()) {
|
||||||
|
substValue = processedOpValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InterpolatorException("Bad substitution operator in: ${" + org + "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
startIdx = nextIdx >= 0 ? nextIdx : variable.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substValue == null) {
|
||||||
|
if (defaultsToEmptyString) {
|
||||||
|
substValue = "";
|
||||||
|
} else {
|
||||||
|
substValue = MARKER + "{" + variable + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return substValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveVariable(
|
||||||
|
String variable,
|
||||||
|
Set<String> cycleMap,
|
||||||
|
Map<String, String> configProps,
|
||||||
|
Function<String, String> callback,
|
||||||
|
BiFunction<String, String, String> postprocessor,
|
||||||
|
boolean defaultsToEmptyString) {
|
||||||
|
|
||||||
|
// Verify that this is not a recursive variable reference
|
||||||
|
if (!cycleMap.add(variable)) {
|
||||||
|
throw new InterpolatorException("recursive variable reference: " + variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
String substValue = null;
|
||||||
|
// Try configuration properties first
|
||||||
|
if (configProps != null) {
|
||||||
|
substValue = configProps.get(variable);
|
||||||
|
}
|
||||||
|
if (substValue == null && !variable.isEmpty() && callback != null) {
|
||||||
|
String s1 = callback.apply(variable);
|
||||||
|
String s2 =
|
||||||
|
doSubstVars(s1, variable, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
|
||||||
|
substValue = postprocessor != null ? postprocessor.apply(variable, s2) : s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the variable from cycle map
|
||||||
|
cycleMap.remove(variable);
|
||||||
|
return substValue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escapes special characters in the given string to prevent unwanted interpolation.
|
* Escapes special characters in the given string to prevent unwanted interpolation.
|
||||||
*
|
*
|
||||||
|
|
|
@ -170,6 +170,38 @@ class DefaultInterpolatorTest {
|
||||||
assertEquals("", props.get("c_cp"));
|
assertEquals("", props.get("c_cp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testXdg() {
|
||||||
|
Map<String, String> props;
|
||||||
|
|
||||||
|
props = new LinkedHashMap<>();
|
||||||
|
props.put("user.home", "/Users/gnodet");
|
||||||
|
props.put(
|
||||||
|
"maven.user.config",
|
||||||
|
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
|
||||||
|
performSubstitution(props);
|
||||||
|
assertEquals("/Users/gnodet/.m2", props.get("maven.user.config"));
|
||||||
|
|
||||||
|
props = new LinkedHashMap<>();
|
||||||
|
props.put("user.home", "/Users/gnodet");
|
||||||
|
props.put(
|
||||||
|
"maven.user.config",
|
||||||
|
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
|
||||||
|
props.put("env.MAVEN_XDG", "true");
|
||||||
|
performSubstitution(props);
|
||||||
|
assertEquals("/Users/gnodet/.config/maven", props.get("maven.user.config"));
|
||||||
|
|
||||||
|
props = new LinkedHashMap<>();
|
||||||
|
props.put("user.home", "/Users/gnodet");
|
||||||
|
props.put(
|
||||||
|
"maven.user.config",
|
||||||
|
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
|
||||||
|
props.put("env.MAVEN_XDG", "true");
|
||||||
|
props.put("env.XDG_CONFIG_HOME", "/Users/gnodet/.xdg/maven");
|
||||||
|
performSubstitution(props);
|
||||||
|
assertEquals("/Users/gnodet/.xdg/maven", props.get("maven.user.config"));
|
||||||
|
}
|
||||||
|
|
||||||
private void performSubstitution(Map<String, String> props) {
|
private void performSubstitution(Map<String, String> props) {
|
||||||
performSubstitution(props, null);
|
performSubstitution(props, null);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue