Ignore mismatched variable delimiters. (FELIX-2336)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@943675 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index 526e20c..9d59424 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -48,8 +48,10 @@
     **/
     private static final String DEFAULT_PROPERTIES_FILE = "default.properties";
 
-    public static String getDefaultProperty(Logger logger, String prop)
+    public static String getDefaultProperty(Logger logger, String name)
     {
+        String value = null;
+
         URL propURL = Util.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE);
         if (propURL != null)
         {
@@ -61,15 +63,11 @@
                 Properties props = new Properties();
                 props.load(is);
                 is.close();
-                // Perform variable substitution for system properties.
-                for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
-                {
-                    String name = (String) e.nextElement();
-                    props.setProperty(name,
-                        Util.substVars(props.getProperty(name), name, null, props));
-                }
-                // Return system packages property.
-                return props.getProperty(prop);
+                // Perform variable substitution for property.
+                value = props.getProperty(name);
+                value = (value != null)
+                    ? Util.substVars(value, name, null, props)
+                    : null;
             }
             catch (Exception ex)
             {
@@ -87,7 +85,7 @@
                     Logger.LOG_ERROR, "Unable to load any configuration properties.", ex);
             }
         }
-        return "";
+        return value;
     }
 
     /**
@@ -491,40 +489,42 @@
         // Find the first ending '}' variable delimiter, which
         // will correspond to the first deepest nested variable
         // placeholder.
-        int stopDelim = val.indexOf(DELIM_STOP);
+        int stopDelim = -1;
+        int startDelim = -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)
+        do
         {
-            int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
-            if ((idx < 0) || (idx > stopDelim))
+            stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
+            // If there is no stopping delimiter, then just return
+            // the value since there is no variable declared.
+            if (stopDelim < 0)
             {
-                break;
+                return val;
             }
-            else if (idx < stopDelim)
+            // Try to find the matching start delimiter by
+            // looping until we find a start delimiter that is
+            // greater than the stop delimiter we have found.
+            startDelim = val.indexOf(DELIM_START);
+            // If there is no starting delimiter, then just return
+            // the value since there is no variable declared.
+            if (startDelim < 0)
             {
-                startDelim = idx;
+                return val;
+            }
+            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 val;
-        }
-        // At this point, we found a stop delimiter without a start,
-        // so throw an exception.
-        else if (((startDelim < 0) || (startDelim > stopDelim))
-            && (stopDelim >= 0))
-        {
-            throw new IllegalArgumentException(
-                "stop delimiter with no start delimiter: "
-                + val);
-        }
+        while ((startDelim > stopDelim) && (stopDelim >= 0));
 
         // At this point, we have found a variable placeholder so
         // we must perform a variable substitution on it.
diff --git a/framework/src/test/java/org/apache/felix/framework/util/UtilTest.java b/framework/src/test/java/org/apache/felix/framework/util/UtilTest.java
new file mode 100644
index 0000000..f3516b8
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/util/UtilTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Properties;
+import junit.framework.TestCase;
+
+public class UtilTest extends TestCase
+{
+    public void testVariableSubstitution()
+    {
+        Properties props = new Properties();
+        props.setProperty("one", "${two}");
+        props.setProperty("two", "2");
+        String v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two}${three}");
+        props.setProperty("two", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("23", v);
+
+        props.clear();
+        props.setProperty("one", "${two${three}}");
+        props.setProperty("two3", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two${three}}");
+        props.setProperty("two3", "2");
+        System.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        System.getProperties().remove("three");
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two}");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("", v);
+
+        props.clear();
+        props.setProperty("one", "{two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two", v);
+
+        props.clear();
+        props.setProperty("one", "{two}");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two}", v);
+
+        props.clear();
+        props.setProperty("one", "${two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("${two", v);
+
+        props.clear();
+        props.setProperty("one", "${two${two}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("${two2", v);
+
+        props.clear();
+        props.setProperty("one", "{two${two}}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two2}", v);
+
+        props.clear();
+        props.setProperty("one", "{two}${two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two}${two", v);
+
+        props.clear();
+        props.setProperty("one", "leading text ${two}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("leading text 2", v);
+
+        props.clear();
+        props.setProperty("one", "${two} trailing text");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2 trailing text", v);
+
+        props.clear();
+        props.setProperty("one", "${two} middle text ${three}");
+        props.setProperty("two", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2 middle text 3", v);
+    }
+}
\ No newline at end of file