You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2010/08/18 11:30:46 UTC
svn commit: r986609 - in /karaf/trunk/shell/config/src:
main/java/org/apache/karaf/shell/config/Properties.java
test/java/org/apache/karaf/shell/config/PropertiesTest.java
Author: gnodet
Date: Wed Aug 18 09:30:46 2010
New Revision: 986609
URL: http://svn.apache.org/viewvc?rev=986609&view=rev
Log:
KARAF-140: Enhance Properties to interpolate values so that overriding interpolated values with their own value don't actually change anything
Modified:
karaf/trunk/shell/config/src/main/java/org/apache/karaf/shell/config/Properties.java
karaf/trunk/shell/config/src/test/java/org/apache/karaf/shell/config/PropertiesTest.java
Modified: karaf/trunk/shell/config/src/main/java/org/apache/karaf/shell/config/Properties.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/config/src/main/java/org/apache/karaf/shell/config/Properties.java?rev=986609&r1=986608&r2=986609&view=diff
==============================================================================
--- karaf/trunk/shell/config/src/main/java/org/apache/karaf/shell/config/Properties.java (original)
+++ karaf/trunk/shell/config/src/main/java/org/apache/karaf/shell/config/Properties.java Wed Aug 18 09:30:46 2010
@@ -18,13 +18,7 @@ package org.apache.karaf.shell.config;
import java.io.*;
import java.net.URL;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
public class Properties extends AbstractMap<String, String> {
@@ -120,11 +114,14 @@ public class Properties extends Abstract
@Override
public String put(String key, String value) {
- Layout l = layout.get(key);
- if (l != null) {
- l.clearValue();
+ String old = storage.put(key, value);
+ if (old == null || !old.equals(value)) {
+ Layout l = layout.get(key);
+ if (l != null) {
+ l.clearValue();
+ }
}
- return storage.put(key, value);
+ return old;
}
@Override
@@ -205,6 +202,7 @@ public class Properties extends Abstract
new ArrayList<String>(reader.getValueLines())));
}
footer = new ArrayList<String>(reader.getCommentLines());
+ performSubstitution(storage);
}
/**
@@ -511,6 +509,150 @@ public class Properties extends Abstract
return false;
}
+ private static final char ESCAPE_CHAR = '\\';
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+
+ private static final String CHECKSUM_SUFFIX = ".checksum";
+
+ /**
+ * Perform substitution on a property set
+ *
+ * @param properties the property set to perform substitution on
+ */
+ public static void performSubstitution(Map<String,String> properties)
+ {
+ for (String name : properties.keySet())
+ {
+ String value = properties.get(name);
+ properties.put(name, substVars(value, name, null, properties));
+ }
+ }
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+ * refers to either a configuration property or a system property,
+ * then the corresponding property value is substituted for the variable
+ * placeholder. Multiple variable placeholders may exist in the
+ * specified value as well as nested variable placeholders, which
+ * are substituted from inner most to outer most. Configuration
+ * properties override system properties.
+ * </p>
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to
+ * detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * property placeholder syntax or a recursive variable reference.
+ **/
+ public static String substVars(String val, String currentKey, Map<String,String> cycleMap, Map<String,String> configProps)
+ throws IllegalArgumentException
+ {
+ if (cycleMap == null)
+ {
+ cycleMap = new HashMap<String,String>();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+ while (stopDelim > 0 && val.charAt(stopDelim - 1) == ESCAPE_CHAR)
+ {
+ stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
+ }
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0)
+ {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim))
+ {
+ break;
+ }
+ else if (idx < stopDelim)
+ {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) || (stopDelim < 0))
+ {
+ return unescape(val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null)
+ {
+ throw new IllegalArgumentException("recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (String) ((configProps != null) ? configProps.get(variable) : null);
+ if (substValue == null)
+ {
+ // Ignore unknown property values.
+ substValue = variable.length() > 0 ? System.getProperty(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
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim) + substValue + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps);
+
+ // Remove escape characters preceding {, } and \
+ val = unescape(val);
+
+ // Return the value.
+ return val;
+ }
+
+ private static String unescape(String val) {
+ int escape = val.indexOf(ESCAPE_CHAR);
+ while (escape >= 0 && escape < val.length() - 1)
+ {
+ char c = val.charAt(escape + 1);
+ if (c == '{' || c == '}' || c == ESCAPE_CHAR)
+ {
+ val = val.substring(0, escape) + val.substring(escape + 1);
+ }
+ escape = val.indexOf(ESCAPE_CHAR, escape + 1);
+ }
+ return val;
+ }
+
/**
* This class is used to read properties lines. These lines do
* not terminate with new-line chars but rather when there is no
Modified: karaf/trunk/shell/config/src/test/java/org/apache/karaf/shell/config/PropertiesTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/config/src/test/java/org/apache/karaf/shell/config/PropertiesTest.java?rev=986609&r1=986608&r2=986609&view=diff
==============================================================================
--- karaf/trunk/shell/config/src/test/java/org/apache/karaf/shell/config/PropertiesTest.java (original)
+++ karaf/trunk/shell/config/src/test/java/org/apache/karaf/shell/config/PropertiesTest.java Wed Aug 18 09:30:46 2010
@@ -17,6 +17,10 @@
package org.apache.karaf.shell.config;
import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.net.URL;
import junit.framework.TestCase;
@@ -24,13 +28,34 @@ import junit.framework.TestCase;
public class PropertiesTest extends TestCase {
public void testLoadSave() throws IOException {
- URL url = getClass().getClassLoader().getResource("OSGI-INF/metatype/metatype.properties");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.println("# ");
+ pw.println("# The Main ");
+ pw.println("# ");
+ pw.println("# Comment ");
+ pw.println("# ");
+ pw.println("");
+ pw.println("# Another comment");
+ pw.println("");
+ pw.println("# A value comment");
+ pw.println("key1 = val1");
+ pw.println("");
+ pw.println("# Another value comment");
+ pw.println("key2 = ${key1}/foo");
+ pw.println("");
+ pw.println("# A third comment");
+ pw.println("key3 = val3");
+ pw.println("");
+
+
Properties props = new Properties();
- props.load(url);
+ props.load(new StringReader(sw.toString()));
props.save(System.err);
System.err.println("=====");
- props.put("storage.name", "foo bar");
+ props.put("key2", props.get("key2"));
+ props.put("key3", "foo");
props.save(System.err);
System.err.println("=====");
}