You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dw...@apache.org on 2018/05/07 11:23:39 UTC

[2/2] lucene-solr:branch_7x: LUCENE-8261: InterpolatedProperties.interpolate and recursive property references.

LUCENE-8261: InterpolatedProperties.interpolate and recursive property references.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/30175b64
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/30175b64
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/30175b64

Branch: refs/heads/branch_7x
Commit: 30175b6410579b6d21a59ccad2a03dd03f89d7c5
Parents: 5d6e47e
Author: Dawid Weiss <dw...@apache.org>
Authored: Mon May 7 13:22:11 2018 +0200
Committer: Dawid Weiss <dw...@apache.org>
Committed: Mon May 7 13:22:45 2018 +0200

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   3 +
 .../dependencies/InterpolatedProperties.java    | 127 ++++++++++++++++---
 2 files changed, 114 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/30175b64/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 252d6e5..542239e 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -88,6 +88,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)
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/30175b64/lucene/tools/src/java/org/apache/lucene/dependencies/InterpolatedProperties.java
----------------------------------------------------------------------
diff --git a/lucene/tools/src/java/org/apache/lucene/dependencies/InterpolatedProperties.java b/lucene/tools/src/java/org/apache/lucene/dependencies/InterpolatedProperties.java
index 159a803..2d5ca41 100644
--- a/lucene/tools/src/java/org/apache/lucene/dependencies/InterpolatedProperties.java
+++ b/lucene/tools/src/java/org/apache/lucene/dependencies/InterpolatedProperties.java
@@ -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));
     }
   }
 }