mirror of https://github.com/apache/lucene.git
LUCENE-8261: InterpolatedProperties.interpolate and recursive property references.
This commit is contained in:
parent
f9942b525b
commit
445c0aa47e
|
@ -183,6 +183,9 @@ Bug Fixes
|
|||
|
||||
Other
|
||||
|
||||
* LUCENE-8261: InterpolatedProperties.interpolate and recursive property
|
||||
references. (Steve Rowe, Dawid Weiss)
|
||||
|
||||
* LUCENE-8228: removed obsolete IndexDeletionPolicy clone() requirements from
|
||||
the javadoc. (Dawid Weiss)
|
||||
|
||||
|
|
|
@ -19,17 +19,28 @@ package org.apache.lucene.dependencies;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Parse a properties file, performing non-recursive Ant-like
|
||||
* property value interpolation, and return the resulting Properties.
|
||||
*/
|
||||
public class InterpolatedProperties extends Properties {
|
||||
private static final Pattern PROPERTY_REFERENCE_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
private static final Pattern PROPERTY_REFERENCE_PATTERN = Pattern.compile("\\$\\{(?<name>[^}]+)\\}");
|
||||
|
||||
/**
|
||||
* Loads the properties file via {@link Properties#load(InputStream)},
|
||||
|
@ -46,26 +57,110 @@ public class InterpolatedProperties extends Properties {
|
|||
*/
|
||||
@Override
|
||||
public void load(Reader reader) throws IOException {
|
||||
super.load(reader);
|
||||
interpolate();
|
||||
Properties p = new Properties();
|
||||
p.load(reader);
|
||||
|
||||
LinkedHashMap<String, String> props = new LinkedHashMap<>();
|
||||
Enumeration<?> e = p.propertyNames();
|
||||
while (e.hasMoreElements()) {
|
||||
String key = (String) e.nextElement();
|
||||
props.put(key, p.getProperty(key));
|
||||
}
|
||||
|
||||
resolve(props).forEach((k, v) -> this.setProperty(k, v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform non-recursive Ant-like property value interpolation
|
||||
*/
|
||||
private void interpolate() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (Map.Entry<?,?> entry : entrySet()) {
|
||||
buffer.setLength(0);
|
||||
Matcher matcher = PROPERTY_REFERENCE_PATTERN.matcher(entry.getValue().toString());
|
||||
private static Map<String,String> resolve(Map<String,String> props) {
|
||||
LinkedHashMap<String, String> resolved = new LinkedHashMap<>();
|
||||
HashSet<String> recursive = new HashSet<>();
|
||||
props.forEach((k, v) -> {
|
||||
resolve(props, resolved, recursive, k, v);
|
||||
});
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static String resolve(Map<String,String> props,
|
||||
LinkedHashMap<String, String> resolved,
|
||||
Set<String> recursive,
|
||||
String key,
|
||||
String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Missing replaced property key: " + key);
|
||||
}
|
||||
|
||||
if (recursive.contains(key)) {
|
||||
throw new IllegalArgumentException("Circular recursive property resolution: " + recursive);
|
||||
}
|
||||
|
||||
if (!resolved.containsKey(key)) {
|
||||
recursive.add(key);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
Matcher matcher = PROPERTY_REFERENCE_PATTERN.matcher(value);
|
||||
while (matcher.find()) {
|
||||
String interpolatedValue = getProperty(matcher.group(1));
|
||||
if (null != interpolatedValue) {
|
||||
matcher.appendReplacement(buffer, interpolatedValue);
|
||||
}
|
||||
String referenced = matcher.group("name");
|
||||
String concrete = resolve(props, resolved, recursive, referenced, props.get(referenced));
|
||||
matcher.appendReplacement(buffer, Matcher.quoteReplacement(concrete));
|
||||
}
|
||||
matcher.appendTail(buffer);
|
||||
setProperty((String) entry.getKey(), buffer.toString());
|
||||
resolved.put(key, buffer.toString());
|
||||
recursive.remove(key);
|
||||
}
|
||||
assert resolved.get(key).equals(value);
|
||||
return resolved.get(key);
|
||||
}
|
||||
|
||||
public static void main(String [] args) {
|
||||
{
|
||||
Map<String, String> props = new LinkedHashMap<>();
|
||||
props.put("a", "${b}");
|
||||
props.put("b", "${c}");
|
||||
props.put("c", "foo");
|
||||
props.put("d", "${a}/${b}/${c}");
|
||||
assertEquals(resolve(props), "a=foo", "b=foo", "c=foo", "d=foo/foo/foo");
|
||||
}
|
||||
|
||||
{
|
||||
Map<String, String> props = new LinkedHashMap<>();
|
||||
props.put("a", "foo");
|
||||
props.put("b", "${a}");
|
||||
assertEquals(resolve(props), "a=foo", "b=foo");
|
||||
}
|
||||
|
||||
{
|
||||
Map<String, String> props = new LinkedHashMap<>();
|
||||
props.put("a", "${b}");
|
||||
props.put("b", "${c}");
|
||||
props.put("c", "${a}");
|
||||
try {
|
||||
resolve(props);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected, circular reference.
|
||||
if (!e.getMessage().contains("Circular recursive")) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Map<String, String> props = new LinkedHashMap<>();
|
||||
props.put("a", "${b}");
|
||||
try {
|
||||
resolve(props);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected, no referenced value.
|
||||
if (!e.getMessage().contains("Missing replaced")) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(Map<String,String> resolved, String... keyValuePairs) {
|
||||
List<String> result = resolved.entrySet().stream().sorted((a, b) -> a.getKey().compareTo(b.getKey()))
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(Collectors.toList());
|
||||
if (!result.equals(Arrays.asList(keyValuePairs))) {
|
||||
throw new AssertionError("Mismatch: \n" + result + "\nExpected: " + Arrays.asList(keyValuePairs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue