You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2017/08/02 04:12:22 UTC

[05/12] lucenenet git commit: Lucene.Net.Support.DictionaryExtensions: Added Load and Store methods from Apache Harmony, so an IDictionary can be used the same way the Properties class is used in Java (saving and loading the same format).

Lucene.Net.Support.DictionaryExtensions: Added Load and Store methods from Apache Harmony, so an IDictionary<string, string> can be used the same way the Properties class is used in Java (saving and loading the same format).


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/cd2d3514
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/cd2d3514
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/cd2d3514

Branch: refs/heads/master
Commit: cd2d3514e3fcda42ec1168667e91657f64aac5f6
Parents: dc67a55
Author: Shad Storhaug <sh...@shadstorhaug.com>
Authored: Mon Jul 31 06:55:19 2017 +0700
Committer: Shad Storhaug <sh...@shadstorhaug.com>
Committed: Wed Aug 2 09:53:09 2017 +0700

----------------------------------------------------------------------
 src/Lucene.Net.Tests/Lucene.Net.Tests.csproj    |   2 +
 .../Support/TestDictionaryExtensions.cs         | 411 +++++++++++++++++++
 .../Support/hyts_PropertiesTest.properties      |  29 ++
 src/Lucene.Net/Support/DictionaryExtensions.cs  | 409 +++++++++++++++++-
 4 files changed, 850 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/cd2d3514/src/Lucene.Net.Tests/Lucene.Net.Tests.csproj
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Lucene.Net.Tests.csproj b/src/Lucene.Net.Tests/Lucene.Net.Tests.csproj
index 7631329..13282e1 100644
--- a/src/Lucene.Net.Tests/Lucene.Net.Tests.csproj
+++ b/src/Lucene.Net.Tests/Lucene.Net.Tests.csproj
@@ -118,6 +118,7 @@
     <None Include="Lucene.Net.snk" />
     <None Include="Lucene.Net.Tests.project.json" />
     <EmbeddedResource Include="Store\LUCENENET521.zip" />
+    <EmbeddedResource Include="Support\hyts_PropertiesTest.properties" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Lucene.Net.Analysis.Common\Lucene.Net.Analysis.Common.csproj">
@@ -522,6 +523,7 @@
     <Compile Include="Support\IO\TestReadOnlyHeapByteBuffer.cs" />
     <Compile Include="Support\IO\TestStreamTokenizer.cs" />
     <Compile Include="Support\SmallObject.cs" />
+    <Compile Include="Support\TestDictionaryExtensions.cs" />
     <Compile Include="Support\TestPriorityQueue.cs" />
     <Compile Include="Support\Threading\TestCloseableThreadLocal.cs" />
     <Compile Include="Support\TestCollections.cs" />

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/cd2d3514/src/Lucene.Net.Tests/Support/TestDictionaryExtensions.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Support/TestDictionaryExtensions.cs b/src/Lucene.Net.Tests/Support/TestDictionaryExtensions.cs
new file mode 100644
index 0000000..540d964
--- /dev/null
+++ b/src/Lucene.Net.Tests/Support/TestDictionaryExtensions.cs
@@ -0,0 +1,411 @@
+using Lucene.Net.Support;
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.Tests.Support
+{
+    public class TestDictionaryExtensions : LuceneTestCase
+    {
+        [Test]
+        public void Test_loadLSystem_IO_Stream_ArgumentNullException()
+        {
+            Dictionary<string, string> p = new Dictionary<string, string>();
+            try
+            {
+                p.Load((Stream)null);
+                fail("should throw NullPointerException");
+            }
+#pragma warning disable 168
+            catch (ArgumentNullException e)
+#pragma warning restore 168
+            {
+                // Expected
+            }
+        }
+
+        /**
+     * @tests java.util.Properties#load(java.io.InputStream)
+     */
+        [Test]
+        public void Test_loadLSystem_IO_Stream()
+        {
+            Dictionary<string, string> prop = new Dictionary<string, string>();
+            using (Stream @is = new MemoryStream(writeProperties()))
+            {
+                prop.Load(@is);
+            }
+            assertEquals("Failed to load correct properties", "harmony.tests", prop.get("test.pkg"));
+            assertNull("Load failed to parse incorrectly", prop
+                    .get("commented.entry"));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("=".getBytes()));
+            assertEquals("Failed to add empty key", "", prop.get(""));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream(" = ".getBytes()));
+            assertEquals("Failed to add empty key2", "", prop.get(""));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream(" a= b".getBytes()));
+            assertEquals("Failed to ignore whitespace", "b", prop.get("a"));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream(" a b".getBytes()));
+            assertEquals("Failed to interpret whitespace as =", "b", prop.get("a"));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("#comment\na=value"
+                    .getBytes("UTF-8")));
+            assertEquals("value", prop.get("a"));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("#\u008d\u00d2\na=\u008d\u00d3"
+                    .getBytes("ISO-8859-1")));
+            assertEquals("Failed to parse chars >= 0x80", "\u008d\u00d3", prop
+                    .get("a"));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream(
+                    "#properties file\r\nfred=1\r\n#last comment"
+                            .getBytes("ISO-8859-1")));
+            assertEquals("Failed to load when last line contains a comment", "1",
+                    prop.get("fred"));
+
+            // Regression tests for HARMONY-5414
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("a=\\u1234z".getBytes()));
+
+            prop = new Dictionary<string, string>();
+            try
+            {
+                prop.Load(new MemoryStream("a=\\u123".getBytes()));
+                fail("should throw IllegalArgumentException");
+            }
+#pragma warning disable 168
+            catch (ArgumentException e)
+#pragma warning restore 168
+            {
+                // Expected
+            }
+
+            prop = new Dictionary<string, string>();
+            try
+            {
+                prop.Load(new MemoryStream("a=\\u123z".getBytes()));
+                fail("should throw IllegalArgumentException");
+            }
+            catch (ArgumentException /*expected*/)
+            {
+                // Expected
+            }
+
+            prop = new Dictionary<string, string>();
+            Dictionary<string, string> expected = new Dictionary<string, string>();
+            expected.Put("a", "\u0000");
+            prop.Load(new MemoryStream("a=\\".getBytes()));
+            assertEquals("Failed to read trailing slash value", expected, prop);
+
+            prop = new Dictionary<string, string>();
+            expected = new Dictionary<string, string>();
+            expected.Put("a", "\u1234\u0000");
+            prop.Load(new MemoryStream("a=\\u1234\\".getBytes()));
+            assertEquals("Failed to read trailing slash value #2", expected, prop);
+
+            prop = new Dictionary<string, string>();
+            expected = new Dictionary<string, string>();
+            expected.Put("a", "q");
+            prop.Load(new MemoryStream("a=\\q".getBytes()));
+            assertEquals("Failed to read slash value #3", expected, prop);
+        }
+
+        /**
+         * @tests java.util.Properties#load(java.io.InputStream)
+         */
+        [Test]
+        public void Test_loadLSystem_IO_Stream_Special()
+        {
+            // Test for method void java.util.Properties.load(java.io.InputStream)
+            Dictionary<string, string> prop = null;
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("=".getBytes()));
+            assertTrue("Failed to add empty key", prop.get("").equals(""));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("=\r\n".getBytes()));
+            assertTrue("Failed to add empty key", prop.get("").equals(""));
+
+            prop = new Dictionary<string, string>();
+            prop.Load(new MemoryStream("=\n\r".getBytes()));
+            assertTrue("Failed to add empty key", prop.get("").equals(""));
+        }
+
+        /**
+         * @tests java.util.Properties#load(java.io.InputStream)
+         */
+        [Test]
+        public void Test_loadLSystem_IO_Stream_subtest0()
+        {
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            using (Stream input = GetType().getResourceAsStream("hyts_PropertiesTest.properties"))
+                props.Load(input);
+
+            assertEquals("1", "\n \t \f", props.getProperty(" \r"));
+            assertEquals("2", "a", props.getProperty("a"));
+            assertEquals("3", "bb as,dn   ", props.getProperty("b"));
+            assertEquals("4", ":: cu", props.getProperty("c\r \t\nu"));
+            assertEquals("5", "bu", props.getProperty("bu"));
+            assertEquals("6", "d\r\ne=e", props.getProperty("d"));
+            assertEquals("7", "fff", props.getProperty("f"));
+            assertEquals("8", "g", props.getProperty("g"));
+            assertEquals("9", "", props.getProperty("h h"));
+            assertEquals("10", "i=i", props.getProperty(" "));
+            assertEquals("11", "   j", props.getProperty("j"));
+            assertEquals("12", "   c", props.getProperty("space"));
+            assertEquals("13", "\\", props.getProperty("dblbackslash"));
+        }
+
+        /**
+     * @tests java.util.Properties#store(java.io.OutputStream, java.lang.String)
+     */
+        [Test]
+        public void Test_storeLSystem_IO_StreamLSystem_String()
+        {
+            Dictionary<string, string> myProps = new Dictionary<string, string>();
+            myProps.Put("Property A", " aye\\\f\t\n\r\b");
+            myProps.Put("Property B", "b ee#!=:");
+            myProps.Put("Property C", "see");
+
+            MemoryStream @out = new MemoryStream();
+            myProps.Store(@out, "A Header");
+            @out.Dispose();
+
+            MemoryStream @in = new MemoryStream(@out.ToArray());
+            Dictionary<string, string> myProps2 = new Dictionary<string, string>();
+            myProps2.Load(@in);
+            @in.Dispose();
+
+            using (var e = myProps.Keys.GetEnumerator())
+            {
+                String nextKey;
+                while (e.MoveNext())
+                {
+                    nextKey = e.Current;
+                    assertTrue("Stored property list not equal to original", myProps2
+                        .getProperty(nextKey).equals(myProps.getProperty(nextKey)));
+                }
+            }
+        }
+
+        /**
+        * if loading from single line like "hello" without "\n\r" neither "=", it
+        * should be same as loading from "hello="
+        */
+        [Test]
+        public void TestLoadSingleLine()
+        {
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            Stream sr = new MemoryStream("hello".getBytes());
+            props.Load(sr);
+            assertEquals(1, props.size());
+        }
+
+        private String comment1 = "comment1";
+
+        private String comment2 = "comment2";
+
+        private void validateOutput(String[] expectStrings, byte[] output)
+        {
+            MemoryStream bais = new MemoryStream(output);
+            TextReader br = new StreamReader(bais,
+                    Encoding.GetEncoding("ISO-8859-1"));
+            foreach (String expectString in expectStrings)
+            {
+                assertEquals(expectString, br.ReadLine());
+            }
+            br.ReadLine();
+            assertNull(br.ReadLine());
+            br.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario0()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario1()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario2()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + '\n' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario3()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + '\r' + comment2);
+            validateOutput(new String[] { "#comment1", "#", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario4()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + '#' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario5()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + '!' + comment2);
+            validateOutput(new String[] { "#comment1", "!comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario6()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + '#' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario7()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + '!' + comment2);
+            validateOutput(new String[] { "#comment1", "!comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario8()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + '\n' + '#' + comment2);
+            validateOutput(new String[] { "#comment1", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario9()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + '\r' + '#' + comment2);
+            validateOutput(new String[] { "#comment1", "#", "#comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario10()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\r' + '\n' + '!' + comment2);
+            validateOutput(new String[] { "#comment1", "!comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+        [Test]
+        public void TestStore_scenario11()
+        {
+            MemoryStream baos = new MemoryStream();
+            Dictionary<string, string> props = new Dictionary<string, string>();
+            props.Store(baos, comment1 + '\n' + '\r' + '!' + comment2);
+            validateOutput(new String[] { "#comment1", "#", "!comment2" },
+                    baos.ToArray());
+            baos.Dispose();
+        }
+
+
+
+        protected byte[] writeProperties()
+        {
+            MemoryStream bout = new MemoryStream();
+            TextWriter ps = new StreamWriter(bout);
+            ps.WriteLine("#commented.entry=Bogus");
+            ps.WriteLine("test.pkg=harmony.tests");
+            ps.WriteLine("test.proj=Automated Tests");
+            ps.Dispose();
+            return bout.ToArray();
+        }
+
+    }
+
+    public static class Extensions
+    {
+        public static byte[] getBytes(this string input)
+        {
+            return Encoding.UTF8.GetBytes(input);
+        }
+
+        public static byte[] getBytes(this string input, string encoding)
+        {
+            return Encoding.GetEncoding(encoding).GetBytes(input);
+        }
+
+        public static string get(this IDictionary<string, string> dict, string key)
+        {
+            string result;
+            dict.TryGetValue(key, out result);
+            return result;
+        }
+
+        public static string getProperty(this IDictionary<string, string> dict, string key)
+        {
+            string result;
+            dict.TryGetValue(key, out result);
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/cd2d3514/src/Lucene.Net.Tests/Support/hyts_PropertiesTest.properties
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Support/hyts_PropertiesTest.properties b/src/Lucene.Net.Tests/Support/hyts_PropertiesTest.properties
new file mode 100644
index 0000000..6c1b950
--- /dev/null
+++ b/src/Lucene.Net.Tests/Support/hyts_PropertiesTest.properties
@@ -0,0 +1,29 @@
+
+
+    
+    		
+   \ \r \n \t \f
+   
+            					
+! dshfjklahfjkldashgjl;as
+     #jdfagdfjagkdjfghksdajfd
+     
+!!properties
+
+a=a
+b bb as,dn   
+c\r\ \t\nu =:: cu
+bu= b\
+		u
+d=d\r\ne=e
+f   :f\
+f\
+			f
+g		g
+h\u0020h
+\   i=i
+j=\   j
+space=\   c
+
+dblbackslash=\\
+                        
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/cd2d3514/src/Lucene.Net/Support/DictionaryExtensions.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net/Support/DictionaryExtensions.cs b/src/Lucene.Net/Support/DictionaryExtensions.cs
index 18acd6a..0f616cb 100644
--- a/src/Lucene.Net/Support/DictionaryExtensions.cs
+++ b/src/Lucene.Net/Support/DictionaryExtensions.cs
@@ -1,4 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
 
 namespace Lucene.Net.Support
 {
@@ -50,5 +53,409 @@ namespace Lucene.Net.Support
             dict[key] = value;
             return oldValue;
         }
+
+        private static readonly int NONE = 0, SLASH = 1, UNICODE = 2, CONTINUE = 3,
+            KEY_DONE = 4, IGNORE = 5;
+        private static string lineSeparator = Environment.NewLine;
+
+
+        // LUCENENET NOTE: Sourced from Apache Harmony:
+
+        /// <summary>
+        /// Loads properties from the specified <see cref="Stream"/>. The encoding is
+        /// ISO8859-1. 
+        /// </summary>
+        /// <remarks>
+        /// The Properties file is interpreted according to the
+        /// following rules:
+        /// <list type="bullet">
+        ///     <item><description>
+        ///         Empty lines are ignored.
+        ///     </description></item>
+        ///     <item><description>
+        ///         Lines starting with either a "#" or a "!" are comment lines and are
+        ///         ignored.
+        ///     </description></item>
+        ///     <item><description>
+        ///         A backslash at the end of the line escapes the following newline
+        ///         character ("\r", "\n", "\r\n"). If there's a whitespace after the
+        ///         backslash it will just escape that whitespace instead of concatenating
+        ///         the lines. This does not apply to comment lines.
+        ///     </description></item>
+        ///     <item><description>
+        ///         A property line consists of the key, the space between the key and
+        ///         the value, and the value. The key goes up to the first whitespace, "=" or
+        ///         ":" that is not escaped. The space between the key and the value contains
+        ///         either one whitespace, one "=" or one ":" and any number of additional
+        ///         whitespaces before and after that character. The value starts with the
+        ///         first character after the space between the key and the value.
+        ///     </description></item>
+        ///     <item><description>
+        ///         Following escape sequences are recognized: "\ ", "\\", "\r", "\n",
+        ///         "\!", "\#", "\t", "\b", "\f", and "&#92;uXXXX" (unicode character).
+        ///     </description></item>
+        /// </list>
+        /// <para/>
+        /// This method is to mimic and interoperate with the Properties class in Java, which
+        /// is essentially a string dictionary that natively supports importing and exporting to this format.
+        /// </remarks>
+        /// <param name="dict">This dictionary.</param>
+        /// <param name="input">The <see cref="Stream"/>.</param>
+        /// <exception cref="IOException">If error occurs during reading from the <see cref="Stream"/>.</exception>
+        public static void Load(this IDictionary<string, string> dict, Stream input)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException("input");
+            }
+            lock (dict)
+            {
+                int mode = NONE, unicode = 0, count = 0;
+                char nextChar;
+                char[] buf = new char[40];
+                int offset = 0, keyLength = -1, intVal;
+                bool firstChar = true;
+                Stream bis = input;
+
+                while (true)
+                {
+                    intVal = bis.ReadByte();
+                    if (intVal == -1)
+                    {
+                        // if mode is UNICODE but has less than 4 hex digits, should
+                        // throw an IllegalArgumentException
+                        // luni.08=Invalid Unicode sequence: expected format \\uxxxx
+                        if (mode == UNICODE && count < 4)
+                        {
+                            throw new ArgumentException("Invalid Unicode sequence: expected format \\uxxxx"); //$NON-NLS-1$
+                        }
+                        // if mode is SLASH and no data is read, should append '\u0000'
+                        // to buf
+                        if (mode == SLASH)
+                        {
+                            buf[offset++] = '\u0000';
+                        }
+                        break;
+                    }
+                    nextChar = (char)(intVal & 0xff);
+
+                    if (offset == buf.Length)
+                    {
+                        char[] newBuf = new char[buf.Length * 2];
+                        System.Array.Copy(buf, 0, newBuf, 0, offset);
+                        buf = newBuf;
+                    }
+                    if (mode == UNICODE)
+                    {
+                        int digit = Character.Digit(nextChar, 16);
+                        if (digit >= 0)
+                        {
+                            unicode = (unicode << 4) + digit;
+                            if (++count < 4)
+                            {
+                                continue;
+                            }
+                        }
+                        else if (count <= 4)
+                        {
+                            // luni.09=Invalid Unicode sequence: illegal character
+                            throw new ArgumentException("Invalid Unicode sequence: illegal character"); //$NON-NLS-1$
+                        }
+                        mode = NONE;
+                        buf[offset++] = (char)unicode;
+                        if (nextChar != '\n')
+                        {
+                            continue;
+                        }
+                    }
+                    if (mode == SLASH)
+                    {
+                        mode = NONE;
+                        switch (nextChar)
+                        {
+                            case '\r':
+                                mode = CONTINUE; // Look for a following \n
+                                continue;
+                            case '\n':
+                                mode = IGNORE; // Ignore whitespace on the next line
+                                continue;
+                            case 'b':
+                                nextChar = '\b';
+                                break;
+                            case 'f':
+                                nextChar = '\f';
+                                break;
+                            case 'n':
+                                nextChar = '\n';
+                                break;
+                            case 'r':
+                                nextChar = '\r';
+                                break;
+                            case 't':
+                                nextChar = '\t';
+                                break;
+                            case 'u':
+                                mode = UNICODE;
+                                unicode = count = 0;
+                                continue;
+                        }
+                    }
+                    else
+                    {
+                        switch (nextChar)
+                        {
+                            case '#':
+                            case '!':
+                                if (firstChar)
+                                {
+                                    while (true)
+                                    {
+                                        intVal = bis.ReadByte();
+                                        if (intVal == -1)
+                                        {
+                                            break;
+                                        }
+                                        // & 0xff not required
+                                        nextChar = (char)intVal;
+                                        if (nextChar == '\r' || nextChar == '\n')
+                                        {
+                                            break;
+                                        }
+                                    }
+                                    continue;
+                                }
+                                break;
+                            case '\n':
+                                if (mode == CONTINUE)
+                                { // Part of a \r\n sequence
+                                    mode = IGNORE; // Ignore whitespace on the next line
+                                    continue;
+                                }
+                                // fall into the next case
+                                mode = NONE;
+                                firstChar = true;
+                                if (offset > 0 || (offset == 0 && keyLength == 0))
+                                {
+                                    if (keyLength == -1)
+                                    {
+                                        keyLength = offset;
+                                    }
+                                    string temp = new string(buf, 0, offset);
+                                    dict.Put(temp.Substring(0, keyLength), temp
+                                            .Substring(keyLength));
+                                }
+                                keyLength = -1;
+                                offset = 0;
+                                continue;
+                            case '\r':
+                                mode = NONE;
+                                firstChar = true;
+                                if (offset > 0 || (offset == 0 && keyLength == 0))
+                                {
+                                    if (keyLength == -1)
+                                    {
+                                        keyLength = offset;
+                                    }
+                                    string temp = new string(buf, 0, offset);
+                                    dict.Put(temp.Substring(0, keyLength), temp
+                                            .Substring(keyLength));
+                                }
+                                keyLength = -1;
+                                offset = 0;
+                                continue;
+                            case '\\':
+                                if (mode == KEY_DONE)
+                                {
+                                    keyLength = offset;
+                                }
+                                mode = SLASH;
+                                continue;
+                            case ':':
+                            case '=':
+                                if (keyLength == -1)
+                                { // if parsing the key
+                                    mode = NONE;
+                                    keyLength = offset;
+                                    continue;
+                                }
+                                break;
+                        }
+                        if (nextChar < 256 && char.IsWhiteSpace(nextChar))
+                        {
+                            if (mode == CONTINUE)
+                            {
+                                mode = IGNORE;
+                            }
+                            // if key length == 0 or value length == 0
+                            if (offset == 0 || offset == keyLength || mode == IGNORE)
+                            {
+                                continue;
+                            }
+                            if (keyLength == -1)
+                            { // if parsing the key
+                                mode = KEY_DONE;
+                                continue;
+                            }
+                        }
+                        if (mode == IGNORE || mode == CONTINUE)
+                        {
+                            mode = NONE;
+                        }
+                    }
+                    firstChar = false;
+                    if (mode == KEY_DONE)
+                    {
+                        keyLength = offset;
+                        mode = NONE;
+                    }
+                    buf[offset++] = nextChar;
+                }
+                if (keyLength == -1 && offset > 0)
+                {
+                    keyLength = offset;
+                }
+                if (keyLength >= 0)
+                {
+                    string temp = new string(buf, 0, offset);
+                    dict.Put(temp.Substring(0, keyLength), temp.Substring(keyLength));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Stores the mappings in this Properties to the specified
+        /// <see cref="Stream"/>, putting the specified comment at the beginning. The
+        /// output from this method is suitable for being read by the
+        /// <see cref="Load(IDictionary{string, string}, Stream)"/> method.
+        /// </summary>
+        /// <param name="dict">This dictionary.</param>
+        /// <param name="output">The output <see cref="Stream"/> to write to.</param>
+        /// <param name="comments">The comments to put at the beginning.</param>
+        /// <exception cref="IOException">If an error occurs during the write to the <see cref="Stream"/>.</exception>
+        /// <exception cref="InvalidCastException">If the key or value of a mapping is not a <see cref="string"/>.</exception>
+        public static void Store(this IDictionary<string, string> dict, Stream output, string comments)
+        {
+            lock (dict)
+            {
+                StreamWriter writer = new StreamWriter(output, Encoding.GetEncoding("iso-8859-1")); //$NON-NLS-1$
+                if (comments != null)
+                {
+                    WriteComments(writer, comments);
+                }
+                writer.Write('#');
+                writer.Write(new DateTime().ToString("yyyy-MM-dd"));
+                writer.Write(lineSeparator);
+
+                StringBuilder buffer = new StringBuilder(200);
+                foreach (var entry in dict)
+                {
+                    string key = entry.Key;
+                    DumpString(buffer, key, true);
+                    buffer.Append('=');
+                    DumpString(buffer, entry.Value, false);
+                    buffer.Append(lineSeparator);
+                    writer.Write(buffer.ToString());
+                    buffer.Length = 0;
+                }
+                writer.Flush();
+            }
+        }
+
+        private static void WriteComments(TextWriter writer, string comments)
+        {
+            writer.Write('#');
+            char[] chars = comments.ToCharArray();
+            for (int index = 0; index < chars.Length; index++)
+            {
+                if (chars[index] == '\r' || chars[index] == '\n')
+                {
+                    int indexPlusOne = index + 1;
+                    if (chars[index] == '\r' && indexPlusOne < chars.Length
+                            && chars[indexPlusOne] == '\n')
+                    {
+                        // "\r\n"
+                        continue;
+                    }
+                    writer.Write(lineSeparator);
+                    if (indexPlusOne < chars.Length
+                            && (chars[indexPlusOne] == '#' || chars[indexPlusOne] == '!'))
+                    {
+                        // return char with either '#' or '!' afterward
+                        continue;
+                    }
+                    writer.Write('#');
+                }
+                else
+                {
+                    writer.Write(chars[index]);
+                }
+            }
+            writer.Write(lineSeparator);
+        }
+
+        private static void DumpString(StringBuilder buffer, string str, bool isKey)
+        {
+            int index = 0, length = str.Length;
+            if (!isKey && index < length && str[index] == ' ')
+            {
+                buffer.Append("\\ "); //$NON-NLS-1$
+                index++;
+            }
+
+            for (; index < length; index++)
+            {
+                char ch = str[index];
+                switch (ch)
+                {
+                    case '\t':
+                        buffer.Append("\\t"); //$NON-NLS-1$
+                        break;
+                    case '\n':
+                        buffer.Append("\\n"); //$NON-NLS-1$
+                        break;
+                    case '\f':
+                        buffer.Append("\\f"); //$NON-NLS-1$
+                        break;
+                    case '\r':
+                        buffer.Append("\\r"); //$NON-NLS-1$
+                        break;
+                    default:
+                        if ("\\#!=:".IndexOf(ch) >= 0 || (isKey && ch == ' '))
+                        {
+                            buffer.Append('\\');
+                        }
+                        if (ch >= ' ' && ch <= '~')
+                        {
+                            buffer.Append(ch);
+                        }
+                        else
+                        {
+                            buffer.Append(ToHexaDecimal(ch));
+                        }
+                        break;
+                }
+            }
+        }
+
+        private static char[] ToHexaDecimal(int ch)
+        {
+            char[] hexChars = { '\\', 'u', '0', '0', '0', '0' };
+            int hexChar, index = hexChars.Length, copyOfCh = ch;
+            do
+            {
+                hexChar = copyOfCh & 15;
+                if (hexChar > 9)
+                {
+                    hexChar = hexChar - 10 + 'A';
+                }
+                else
+                {
+                    hexChar += '0';
+                }
+                hexChars[--index] = (char)hexChar;
+                //} while ((copyOfCh >>>= 4) != 0);
+            } while ((copyOfCh = (int)((uint)copyOfCh >> 4)) != 0);
+            return hexChars;
+        }
     }
 }
\ No newline at end of file