Apply patch FELIX-2887 to add some new functionality to property handling.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1172744 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/utils/src/main/java/org/apache/felix/utils/properties/Properties.java b/utils/src/main/java/org/apache/felix/utils/properties/Properties.java
index 6c6743c..a0cbf36 100644
--- a/utils/src/main/java/org/apache/felix/utils/properties/Properties.java
+++ b/utils/src/main/java/org/apache/felix/utils/properties/Properties.java
@@ -29,7 +29,17 @@
 import java.io.Reader;
 import java.io.Writer;
 import java.net.URL;
-import java.util.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * <p>
@@ -57,7 +67,11 @@
     private static final String DEFAULT_ENCODING = "ISO-8859-1";
 
     /** Constant for the platform specific line separator.*/
-    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+    private static final String LINE_SEPARATOR = AccessController.doPrivileged(new PrivilegedAction<String>() {
+            public String run() {
+                return System.getProperty("line.separator");
+            }
+        });
 
     /** Constant for the radix of hex numbers.*/
     private static final int HEX_RADIX = 16;
@@ -65,8 +79,8 @@
     /** Constant for the length of a unicode literal.*/
     private static final int UNICODE_LEN = 4;
 
-    private Map<String,String> storage = new LinkedHashMap<String,String>();
-    private Map<String,Layout> layout = new LinkedHashMap<String,Layout>();
+    private final Map<String,String> storage = new LinkedHashMap<String,String>();
+    private final Map<String,Layout> layout = new LinkedHashMap<String,Layout>();
     private List<String> header;
     private List<String> footer;
     private File location;
@@ -203,6 +217,50 @@
         return old;
     }
 
+    public String put(String key, List<String> commentLines, List<String> valueLines) {
+        commentLines = new ArrayList<String>(commentLines);
+        valueLines = new ArrayList<String>(valueLines);
+        String escapedKey = escapeKey(key);
+        int lastLine = valueLines.size() - 1;
+        if (valueLines.isEmpty()) {
+            valueLines.add(escapedKey + "=");
+        } else if (!valueLines.get(0).trim().startsWith(escapedKey)) {
+            valueLines.set(0, escapedKey + " = " + escapeJava(valueLines.get(0)) + (0 < lastLine? "\\": ""));
+        }
+        for (int i = 1; i < valueLines.size(); i++) {
+            valueLines.set(i, escapeJava(valueLines.get(i)) + (i < lastLine? "\\": ""));
+        }
+        StringBuilder value = new StringBuilder();
+        for (String line: valueLines) {
+            value.append(line);
+        }
+        this.layout.put(key, new Layout(commentLines, valueLines));
+        return storage.put(key, unescapeJava(value.toString()));
+    }
+
+    public String put(String key, List<String> commentLines, String value) {
+        commentLines = new ArrayList<String>(commentLines);
+        this.layout.put(key, new Layout(commentLines, null));
+        return storage.put(key, value);
+    }
+
+    public String put(String key, String comment, String value) {
+        return put(key, Collections.singletonList(comment), value);
+    }
+
+    public List<String> getRaw(String key) {
+        if (layout.containsKey(key)) {
+            if (layout.get(key).getValueLines() != null) {
+                return new ArrayList<String>(layout.get(key).getValueLines());
+            }
+        }
+        List<String> result = new ArrayList<String>();
+        if (storage.containsKey(key)) {
+            result.add(storage.get(key));
+        }
+        return result;
+    }
+
     @Override
     public String remove(Object key) {
         Layout l = layout.get(key);
@@ -589,6 +647,35 @@
     }
 
     /**
+     * Escape the separators in the key.
+     *
+     * @param key the key
+     * @return the escaped key
+     */
+    private static String escapeKey(String key)
+    {
+        StringBuffer newkey = new StringBuffer();
+
+        for (int i = 0; i < key.length(); i++)
+        {
+            char c = key.charAt(i);
+
+            if (contains(SEPARATORS, c) || contains(WHITE_SPACE, c))
+            {
+                // escape the separator
+                newkey.append('\\');
+                newkey.append(c);
+            }
+            else
+            {
+                newkey.append(c);
+            }
+        }
+
+        return newkey.toString();
+    }
+
+    /**
      * This class is used to read properties lines. These lines do
      * not terminate with new-line chars but rather when there is no
      * backslash sign a the end of the line.  This is used to
@@ -597,10 +684,10 @@
     public static class PropertiesReader extends LineNumberReader
     {
         /** Stores the comment lines for the currently processed property.*/
-        private List<String> commentLines;
+        private final List<String> commentLines;
 
         /** Stores the value lines for the currently processed property.*/
-        private List<String> valueLines;
+        private final List<String> valueLines;
 
         /** Stores the name of the last read property.*/
         private String propertyName;
@@ -894,35 +981,6 @@
         }
 
         /**
-         * Escape the separators in the key.
-         *
-         * @param key the key
-         * @return the escaped key
-         */
-        private String escapeKey(String key)
-        {
-            StringBuffer newkey = new StringBuffer();
-
-            for (int i = 0; i < key.length(); i++)
-            {
-                char c = key.charAt(i);
-
-                if (contains(SEPARATORS, c) || contains(WHITE_SPACE, c))
-                {
-                    // escape the separator
-                    newkey.append('\\');
-                    newkey.append(c);
-                }
-                else
-                {
-                    newkey.append(c);
-                }
-            }
-
-            return newkey.toString();
-        }
-
-        /**
          * Helper method for writing a line with the platform specific line
          * ending.
          *
diff --git a/utils/src/test/java/org/apache/felix/utils/properties/PropertiesTest.java b/utils/src/test/java/org/apache/felix/utils/properties/PropertiesTest.java
index 3f06738..fa8c48d 100644
--- a/utils/src/test/java/org/apache/felix/utils/properties/PropertiesTest.java
+++ b/utils/src/test/java/org/apache/felix/utils/properties/PropertiesTest.java
@@ -20,7 +20,10 @@
 import java.io.PrintWriter;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
 
+import com.sun.org.apache.xerces.internal.impl.dv.ValidatedInfo;
 import junit.framework.TestCase;
 
 /**
@@ -33,6 +36,13 @@
 public class PropertiesTest extends TestCase {
 
     private final static String TEST_PROPERTIES_FILE = "test.properties";
+    private final static String LINE_SEPARATOR = System.getProperty("line.separator");
+    private static final String COMMENT = "# comment";
+    private static final String KEY1 = "mvn:foo/bar";
+    private static final String KEY1A = "mvn\\:foo/bar";
+    private static final String KEY2 = "foo:bar:version:type:classifier";
+    private static final String KEY2A = "foo\\:bar\\:version\\:type\\:classifier";
+    private static final String VALUE1 = "value";
 
     private Properties properties;
 
@@ -105,4 +115,44 @@
         properties.store(System.err, null);
         System.err.println("====");
     }
+
+    private static final String RESULT1 = COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1 + LINE_SEPARATOR;
+
+    public void testSaveComment1() throws Exception {
+        properties.put(KEY1, COMMENT, VALUE1);
+        StringWriter sw = new StringWriter();
+        properties.save(sw);
+        assertTrue(sw.toString(), sw.toString().endsWith(RESULT1));
+    }
+
+    private static final String RESULT1A = COMMENT + LINE_SEPARATOR + KEY2A + " = " + VALUE1 + LINE_SEPARATOR;
+
+    public void testSaveComment1a() throws Exception {
+        properties.put(KEY2, COMMENT, VALUE1);
+        StringWriter sw = new StringWriter();
+        properties.save(sw);
+        assertTrue(sw.toString(), sw.toString().endsWith(RESULT1A));
+    }
+
+    private static final String RESULT2 = COMMENT + LINE_SEPARATOR + COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1 + LINE_SEPARATOR;
+
+    public void testSaveComment2() throws Exception {
+        properties.put(KEY1, Arrays.asList(new String[] {COMMENT, COMMENT}), VALUE1);
+        StringWriter sw = new StringWriter();
+        properties.save(sw);
+        assertTrue(sw.toString(), sw.toString().endsWith(RESULT2));
+    }
+
+    private static final String RESULT3 = COMMENT + LINE_SEPARATOR + COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1 + "\\" + LINE_SEPARATOR+ VALUE1 + LINE_SEPARATOR;
+
+    public void testSaveComment3() throws Exception {
+        properties.put(KEY1, Arrays.asList(new String[] {COMMENT, COMMENT}), Arrays.asList(new String[] {VALUE1, VALUE1}));
+        StringWriter sw = new StringWriter();
+        properties.save(sw);
+        assertTrue(sw.toString(), sw.toString().endsWith(RESULT3));
+        List<String> rawValue = properties.getRaw(KEY1);
+        assertEquals(2, rawValue.size());
+        assertEquals(KEY1A + " = " + VALUE1 + "\\", rawValue.get(0));
+        assertEquals(VALUE1, rawValue.get(1));
+    }
 }