[tomboy] Import Hyena.Json from Banshee.



commit 450c7fd5b100dd19581d60fc884b6e549fa048bf
Author: Sandy Armstrong <sanfordarmstrong gmail com>
Date:   Fri May 15 18:47:00 2009 -0700

    Import Hyena.Json from Banshee.
---
 .../WebSyncService/Hyena.Json/Deserializer.cs      |  163 ++++++++++
 .../WebSyncService/Hyena.Json/IJsonCollection.cs   |   39 +++
 .../Addins/WebSyncService/Hyena.Json/JsonArray.cs  |   60 ++++
 .../Addins/WebSyncService/Hyena.Json/JsonObject.cs |   60 ++++
 .../Hyena.Json/Tests/DeserializerTests.cs          |  102 +++++++
 .../Hyena.Json/Tests/TokenizerTests.cs             |  207 +++++++++++++
 Tomboy/Addins/WebSyncService/Hyena.Json/Token.cs   |  111 +++++++
 .../Addins/WebSyncService/Hyena.Json/TokenType.cs  |   51 ++++
 .../Addins/WebSyncService/Hyena.Json/Tokenizer.cs  |  316 ++++++++++++++++++++
 9 files changed, 1109 insertions(+), 0 deletions(-)

diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/Deserializer.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/Deserializer.cs
new file mode 100644
index 0000000..15afe8c
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/Deserializer.cs
@@ -0,0 +1,163 @@
+// 
+// Deserializer.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+
+namespace Hyena.Json
+{
+    public class Deserializer
+    {
+        private Tokenizer tokenizer = new Tokenizer ();
+        
+        public Deserializer () { }
+        public Deserializer (string input) { SetInput (input); }
+        public Deserializer (Stream stream) { SetInput (stream); }
+        public Deserializer (StreamReader reader) { SetInput (reader); }
+        
+        public Deserializer SetInput (StreamReader reader)
+        {
+            tokenizer.SetInput (reader);
+            return this;
+        }
+        
+        public Deserializer SetInput (Stream stream)
+        {
+            tokenizer.SetInput (stream);
+            return this;
+        }
+        
+        public Deserializer SetInput (string input)
+        {
+            tokenizer.SetInput (input);
+            return this;
+        }
+        
+        public object Deserialize ()
+        {
+            Token token = CheckScan (TokenType.Value, true);
+            if (token == null) {
+                return null;
+            }
+            
+            return Parse (token);
+        }
+        
+        private object Parse (Token token)
+        {
+            if (token.Type == TokenType.ObjectStart) {
+                return ParseObject ();
+            } else if (token.Type == TokenType.ArrayStart) {
+                return ParseArray ();
+            }
+            
+            return token.Value;
+        }
+        
+        private JsonObject ParseObject ()
+        {
+            JsonObject obj = new JsonObject ();
+            
+            while (true) {
+                Token key = CheckScan (TokenType.String | TokenType.ObjectFinish);
+                if (key.Type == TokenType.ObjectFinish) {
+                    break;
+                }
+                
+                CheckScan (TokenType.Colon);
+                Token value = CheckScan (TokenType.Value);
+                
+                object value_val = value.Value;
+                if (value.Type == TokenType.ArrayStart) {
+                    value_val = ParseArray ();
+                } else if (value.Type == TokenType.ObjectStart) {
+                    value_val = ParseObject ();
+                }
+                
+                obj.Add ((string)key.Value, value_val);
+                
+                Token token = CheckScan (TokenType.Comma | TokenType.ObjectFinish);
+                if (token.Type == TokenType.ObjectFinish) {
+                     break;
+                }
+            }
+            
+            return obj;
+        }
+        
+        private JsonArray ParseArray ()
+        {
+            JsonArray array = new JsonArray ();
+            
+            while (true) {
+                Token value = CheckScan (TokenType.Value | TokenType.ArrayFinish);
+                if (value.Type == TokenType.ArrayFinish) {
+                    break;
+                }
+                
+                array.Add (Parse (value));
+                
+                Token token = CheckScan (TokenType.Comma | TokenType.ArrayFinish);
+                if (token.Type == TokenType.ArrayFinish) {
+                     break;
+                }
+            }
+            
+            return array;
+        }
+        
+        private Token CheckScan (TokenType expected)
+        {
+            return CheckScan (expected, false);
+        }
+        
+        private Token CheckScan (TokenType expected, bool eofok)
+        {
+            Token token = tokenizer.Scan ();
+            if (token == null && eofok) {
+                return null;
+            } else if (token == null) {
+                UnexpectedEof (expected);
+            } else if ((expected & token.Type) == 0) {
+                UnexpectedToken (expected, token);
+            }
+            return token;
+        }
+        
+        private void UnexpectedToken (TokenType expected, Token got)
+        {
+            throw new ApplicationException (String.Format ("Unexpected token {0} at [{1}:{2}]; expected {3}", 
+                got.Type, got.SourceLine, got.SourceColumn, expected));
+        }
+        
+        private void UnexpectedEof (TokenType expected)
+        {
+            throw new ApplicationException (String.Format ("Unexpected End of File; expected {0}", expected));
+        }
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/IJsonCollection.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/IJsonCollection.cs
new file mode 100644
index 0000000..4d46bf7
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/IJsonCollection.cs
@@ -0,0 +1,39 @@
+// 
+// IJsonCollection.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+
+namespace Hyena.Json
+{
+    public interface IJsonCollection : ICollection
+    {
+        void Dump ();
+        void Dump (int count);
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/JsonArray.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/JsonArray.cs
new file mode 100644
index 0000000..f2288bf
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/JsonArray.cs
@@ -0,0 +1,60 @@
+// 
+// JsonArray.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace Hyena.Json
+{
+    public class JsonArray : List<object>, IJsonCollection
+    {
+        public void Dump ()
+        {
+            Dump (1);
+        }
+        
+        public void Dump (int levels)
+        {
+            if (Count == 0) {
+                Console.WriteLine ("[ ]");
+                return;
+            }
+        
+            Console.WriteLine ("[");
+            foreach (object item in this) {
+                Console.Write (String.Empty.PadLeft (levels * 2, ' '));
+                if (item is IJsonCollection) {
+                    ((IJsonCollection)item).Dump (levels + 1);
+                } else {
+                    Console.WriteLine (item);
+                }
+            }
+            Console.WriteLine ("{0}]", String.Empty.PadLeft ((levels - 1) * 2, ' '));
+        }
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/JsonObject.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/JsonObject.cs
new file mode 100644
index 0000000..98d32c4
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/JsonObject.cs
@@ -0,0 +1,60 @@
+// 
+// JsonObject.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace Hyena.Json
+{
+    public class JsonObject : Dictionary<string, object>, IJsonCollection
+    {
+        public void Dump ()
+        {
+            Dump (1);
+        }
+        
+        public void Dump (int levels)
+        {
+            if (Count == 0) {
+                Console.WriteLine ("{ }");
+                return;
+            }
+        
+            Console.WriteLine ("{");
+            foreach (KeyValuePair<string, object> item in this) {
+                Console.Write ("{0}\"{1}\" : ", String.Empty.PadLeft (levels * 2, ' '), item.Key);
+                if (item.Value is IJsonCollection) {
+                    ((IJsonCollection)item.Value).Dump (levels + 1);
+                } else {
+                    Console.WriteLine (item.Value);
+                }
+            }
+            Console.WriteLine ("{0}}}", String.Empty.PadLeft ((levels - 1) * 2, ' '));
+        }
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/DeserializerTests.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/DeserializerTests.cs
new file mode 100644
index 0000000..e5c9927
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/DeserializerTests.cs
@@ -0,0 +1,102 @@
+//
+// DeserializerTests.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if ENABLE_TESTS
+
+using System;
+using System.Reflection;
+using NUnit.Framework;
+
+using Hyena.Json;
+
+namespace Hyena.Json.Tests
+{
+    [TestFixture]
+    public class DeserializerTests : Hyena.Tests.TestBase
+    {
+        private Deserializer deserializer;
+        
+        [TestFixtureSetUp]
+        public void Setup ()
+        {
+            deserializer = new Deserializer ();
+        }
+        
+        [Test]
+        public void Literal ()
+        {
+            Assert.AreEqual ("hello", (string)deserializer.SetInput ("\"hello\"").Deserialize ());
+            Assert.AreEqual (-1.76e-3, (double)deserializer.SetInput ("-1.76e-3").Deserialize ());
+            Assert.AreEqual (null, deserializer.SetInput ("null").Deserialize ());
+            Assert.AreEqual (true, (bool)deserializer.SetInput ("true").Deserialize ());
+            Assert.AreEqual (false, (bool)deserializer.SetInput ("false").Deserialize ());
+        }
+        
+        [Test]
+        public void Array ()
+        {
+            JsonArray array = (JsonArray)deserializer.SetInput ("[]").Deserialize ();
+            Assert.AreEqual (0, array.Count);
+            
+            array = (JsonArray)deserializer.SetInput ("[[]]").Deserialize ();
+            Assert.AreEqual (1, array.Count);
+            Assert.AreEqual (0, ((JsonArray)array[0]).Count);
+            
+            array = (JsonArray)deserializer.SetInput ("[[true,[]]]").Deserialize ();
+            Assert.AreEqual (1, array.Count);
+            Assert.AreEqual (2, ((JsonArray)array[0]).Count);
+            Assert.AreEqual (0, ((JsonArray)((JsonArray)array[0])[1]).Count);
+            
+            array = (JsonArray)deserializer.SetInput ("[\"a\", 1.0, true]").Deserialize ();
+            Assert.AreEqual (3, array.Count);
+            Assert.AreEqual ("a", (string)array[0]);
+            Assert.AreEqual (1, (double)array[1]);
+            Assert.AreEqual (true, (bool)array[2]);
+        }
+        
+        [Test]
+        public void Object ()
+        {
+            JsonObject obj = (JsonObject)deserializer.SetInput ("{}").Deserialize ();
+            Assert.AreEqual (0, obj.Count);
+            
+            obj = (JsonObject)deserializer.SetInput ("{\"a\":{}}").Deserialize ();
+            Assert.AreEqual (1, obj.Count);
+            Assert.AreEqual (0, ((JsonObject)obj["a"]).Count);
+            
+            obj = (JsonObject)deserializer.SetInput ("{\"a\":[{\"b\":false},\"c\"]}").Deserialize ();
+            Assert.AreEqual (1, obj.Count);
+            JsonArray arr = (JsonArray)obj["a"];
+            Assert.AreEqual (2, arr.Count);
+            Assert.AreEqual (false, ((JsonObject)arr[0])["b"]);
+            Assert.AreEqual ("c", (string)arr[1]);
+        }
+    }
+}
+
+#endif
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/TokenizerTests.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/TokenizerTests.cs
new file mode 100644
index 0000000..696fa95
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/Tests/TokenizerTests.cs
@@ -0,0 +1,207 @@
+//
+// TokenizerTests.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if ENABLE_TESTS
+
+using System;
+using System.Reflection;
+using NUnit.Framework;
+
+using Hyena.Json;
+
+namespace Hyena.Json.Tests
+{
+    [TestFixture]
+    public class TokenizerTests : Hyena.Tests.TestBase
+    {
+        private Tokenizer tokenizer;
+        
+        [TestFixtureSetUp]
+        public void Setup ()
+        {
+            tokenizer = new Tokenizer ();
+        }
+    
+        [Test]
+        public void Whitespace ()
+        {
+            AssertTokenStream ("");
+            AssertTokenStream (" ");
+            AssertTokenStream ("\f\n\r\t ");
+        }
+        
+        [Test]
+        public void BoolNull ()
+        {
+            // Boolean/null tests
+            AssertTokenStream ("true", Token.Bool (true));
+            AssertTokenStream ("false", Token.Bool (false));
+            AssertTokenStream ("null", Token.Null);
+        }
+        
+        [Test]
+        public void NumberInt ()
+        {
+            // Number tests
+            AssertTokenStream ("0", Token.Number (0));
+            AssertTokenStream ("-0", Token.Number (-0));
+            
+            AssertTokenStream ("9", Token.Number (9));
+            AssertTokenStream ("-9", Token.Number (-9));
+            
+            AssertTokenStream ("14", Token.Number (14));
+            AssertTokenStream ("-14", Token.Number (-14));
+            
+            AssertTokenStream ("15309", Token.Number (15309));
+            AssertTokenStream ("-15309", Token.Number (-15309));
+        }
+        
+        [Test]
+        public void NumberFloat ()
+        {
+            AssertTokenStream ("0.0", Token.Number (0.0));
+            AssertTokenStream ("-0.0", Token.Number (-0.0));
+            
+            AssertTokenStream ("1.9", Token.Number (1.9));
+            AssertTokenStream ("-1.9", Token.Number (-1.9));
+            
+            AssertTokenStream ("9.1", Token.Number (9.1));
+            AssertTokenStream ("-9.1", Token.Number (-9.1));
+            
+            AssertTokenStream ("15309.0", Token.Number (15309.0));
+            AssertTokenStream ("15309.9", Token.Number (15309.9));
+            AssertTokenStream ("-15309.01", Token.Number (-15309.01));
+            AssertTokenStream ("-15309.9009", Token.Number (-15309.9009));
+        }
+        
+        [Test]
+        public void NumberExponent ()
+        {
+            AssertTokenStream ("20.6e3", Token.Number (20.6e3));
+            AssertTokenStream ("20.6e+3", Token.Number (20.6e+3));
+            AssertTokenStream ("20.6e-3", Token.Number (20.6e-3));
+            AssertTokenStream ("-20.6e3", Token.Number (-20.6e3));
+            AssertTokenStream ("-20.6e+3", Token.Number (-20.6e+3));
+            AssertTokenStream ("-20.6e-3", Token.Number (-20.6e-3));
+            
+            AssertTokenStream ("1e1", Token.Number (1e1));
+            AssertTokenStream ("1E2", Token.Number (1E2));
+            AssertTokenStream ("1.0e1", Token.Number (1.0e1));
+            AssertTokenStream ("1.0E1", Token.Number (1.0E1));
+        }
+        
+        [Test]
+        public void Strings ()
+        {
+            AssertTokenStream (@"""""", Token.String (""));
+            AssertTokenStream (@"""a""", Token.String ("a"));
+            AssertTokenStream (@"""ab""", Token.String ("ab"));
+            AssertTokenStream (@" ""a b"" ", Token.String ("a b"));
+            AssertTokenStream (@"""\""\""""", Token.String ("\"\""));
+            AssertTokenStream (@" ""a \"" \"" b"" ", Token.String ("a \" \" b"));
+            AssertTokenStream (@"""\ubeef""", Token.String ("\ubeef"));
+            AssertTokenStream (@"""\u00a9""", Token.String ("\u00a9"));
+            AssertTokenStream (@"""\u0000\u0001\u0002""", Token.String ("\u0000\u0001\u0002"));
+            AssertTokenStream (@"""1\uabcdef0""", Token.String ("1\uabcdef0"));
+            AssertTokenStream (@"""\b\f\n\r\t""", Token.String ("\b\f\n\r\t"));
+        }
+        
+        [Test]
+        public void Container ()
+        {
+            AssertTokenStream ("{}", Token.ObjectStart, Token.ObjectFinish);
+            AssertTokenStream ("[]", Token.ArrayStart, Token.ArrayFinish);
+            AssertTokenStream ("{  }", Token.ObjectStart, Token.ObjectFinish);
+            AssertTokenStream ("[  ]", Token.ArrayStart, Token.ArrayFinish);
+            AssertTokenStream ("[{}]", Token.ArrayStart, Token.ObjectStart, Token.ObjectFinish, Token.ArrayFinish);
+            AssertTokenStream ("[[[ { } ]]]", 
+                Token.ArrayStart, Token.ArrayStart, Token.ArrayStart, 
+                Token.ObjectStart, Token.ObjectFinish, 
+                Token.ArrayFinish, Token.ArrayFinish, Token.ArrayFinish);
+        }
+        
+        [Test]
+        public void Array ()
+        {
+            AssertTokenStream ("[1]", Token.ArrayStart, Token.Number (1), Token.ArrayFinish);
+            AssertTokenStream ("[1,0]", Token.ArrayStart, Token.Number (1), Token.Comma, Token.Number (0), Token.ArrayFinish);
+            AssertTokenStream ("[\"a\",true,null]", Token.ArrayStart, Token.String ("a"), Token.Comma, 
+                Token.Bool (true), Token.Comma, Token.Null, Token.ArrayFinish);
+            AssertTokenStream ("[0,1,[[2,[4]],5],6]", Token.ArrayStart, Token.Number (0), Token.Comma, Token.Number (1),
+                 Token.Comma, Token.ArrayStart, Token.ArrayStart, Token.Number (2), Token.Comma, Token.ArrayStart, 
+                 Token.Number (4), Token.ArrayFinish, Token.ArrayFinish, Token.Comma, Token.Number (5), Token.ArrayFinish,
+                 Token.Comma, Token.Number (6), Token.ArrayFinish);
+        }
+        
+        [Test]
+        public void Object ()
+        {
+            AssertTokenStream ("{\"a\":{}}", Token.ObjectStart, Token.String ("a"), Token.Colon, Token.ObjectStart, 
+                Token.ObjectFinish, Token.ObjectFinish);
+            AssertTokenStream ("{\"a\":{\"b\":[],\"c\":false}}", Token.ObjectStart, Token.String ("a"), 
+                Token.Colon, Token.ObjectStart, Token.String ("b"), Token.Colon, Token.ArrayStart, Token.ArrayFinish, 
+                Token.Comma, Token.String ("c"), Token.Colon, Token.Bool (false), Token.ObjectFinish, Token.ObjectFinish);
+            AssertTokenStream ("[{\"a\":{},{}]", Token.ArrayStart, Token.ObjectStart, Token.String ("a"), Token.Colon, 
+                Token.ObjectStart, Token.ObjectFinish, Token.Comma, Token.ObjectStart, Token.ObjectFinish, Token.ArrayFinish);
+        }    
+        
+        private void AssertTokenStream (string input, params Token [] tokens)
+        {
+            int cmp_idx = 0;
+            tokenizer.SetInput (input);
+            
+            while (true) {
+                Token token = tokenizer.Scan ();
+                if (token == null) {
+                    if (cmp_idx != tokens.Length) {
+                        throw new ApplicationException ("Unexpected EOF");
+                    }
+                    break;
+                }
+                
+                Token compare = tokens[cmp_idx++];
+                if (compare.Type != token.Type) {
+                    throw new ApplicationException (String.Format ("TokenTypes do not match (exp {0}, got {1}", 
+                        compare.Type, token.Type));
+                }
+                
+                if (compare.Value == null && token.Value == null) {
+                    continue;
+                }
+                
+                if ((compare.Type == TokenType.Number && (double)compare.Value != (double)token.Value) ||
+                    (compare.Type == TokenType.String && (string)compare.Value != (string)token.Value) ||
+                    (compare.Type == TokenType.Boolean && (bool)compare.Value != (bool)token.Value)) {
+                    throw new ApplicationException ("Token values do not match");
+                }
+            }
+        }
+    }
+}
+
+#endif
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/Token.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/Token.cs
new file mode 100644
index 0000000..cce60c7
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/Token.cs
@@ -0,0 +1,111 @@
+// 
+// Token.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Hyena.Json
+{   
+    internal class Token
+    {
+        public Token (TokenType type) : this (type, null)
+        {
+        }
+        
+        public Token (TokenType type, object value)
+        {
+            this.type = type;
+            this.value = value;
+        }
+        
+        private TokenType type;
+        public TokenType Type {
+            get { return type; }
+        }
+
+        private object value;
+        public object Value {
+            get { return value; }
+            set { this.value = value; }
+        }
+
+        private int source_line;
+        public int SourceLine {
+            get { return source_line; }
+            internal set { source_line = value; }
+        }
+        
+        private int source_column;
+        public int SourceColumn {
+            get { return source_column; }
+            internal set { source_column = value; }
+        }
+        
+        internal static Token ObjectStart {
+            get { return new Token (TokenType.ObjectStart); }
+        }
+        
+        internal static Token ObjectFinish {
+            get { return new Token (TokenType.ObjectFinish); }
+        }
+        
+        internal static Token ArrayStart {
+            get { return new Token (TokenType.ArrayStart); }
+        }
+        
+        internal static Token ArrayFinish {
+            get { return new Token (TokenType.ArrayFinish); }
+        }
+        
+        internal static Token Null {
+            get { return new Token (TokenType.Null); }
+        }
+        
+        internal static Token Comma {
+            get { return new Token (TokenType.Comma); }
+        }
+        
+        internal static Token Colon {
+            get { return new Token (TokenType.Colon); }
+        }
+        
+        internal static Token Number (double value)
+        {
+            return new Token (TokenType.Number, value);
+        }
+        
+        internal static Token String (string value)
+        {
+            return new Token (TokenType.String, value);
+        }
+        
+        internal static Token Bool (bool value)
+        {
+            return new Token (TokenType.Boolean, value);
+        }
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/TokenType.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/TokenType.cs
new file mode 100644
index 0000000..b24a5ca
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/TokenType.cs
@@ -0,0 +1,51 @@
+// 
+// TokenType.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Hyena.Json
+{
+    [Flags]
+    internal enum TokenType
+    {
+        None = 0 << 0,
+        ObjectStart = 1 << 0,
+        ObjectFinish = 1 << 1,
+        ArrayStart = 1 << 2,
+        ArrayFinish = 1 << 3,
+        Boolean = 1 << 4,
+        String = 1 << 5,
+        Null = 1 << 6,
+        Number = 1 << 7,
+        Comma = 1 << 8,
+        Colon = 1 << 9,
+        
+        Literal = String | Number | Boolean | Null,
+        Value = ObjectStart | ArrayStart | Literal
+    }
+}
diff --git a/Tomboy/Addins/WebSyncService/Hyena.Json/Tokenizer.cs b/Tomboy/Addins/WebSyncService/Hyena.Json/Tokenizer.cs
new file mode 100644
index 0000000..f866c0b
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/Hyena.Json/Tokenizer.cs
@@ -0,0 +1,316 @@
+// 
+// Tokenizer.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Text;
+
+namespace Hyena.Json
+{
+    internal class Tokenizer
+    {
+        private StreamReader reader;
+        private StringBuilder string_buffer;
+        
+        private char peek = ' ';
+        private int current_line = 1;
+        private int current_column = 1;
+        private int token_start_line;
+        private int token_start_column;
+        
+        public Tokenizer () { Reset (); }
+        public Tokenizer (string input) { SetInput (input); }
+        public Tokenizer (Stream stream) { SetInput (stream); }
+        public Tokenizer (StreamReader reader) { SetInput (reader); }
+        
+        private void Reset ()
+        {
+            peek = ' ';
+            current_line = 1;
+            current_column = 1;
+            token_start_line = 0;
+            token_start_column = 0;
+        }
+        
+        public void SetInput (StreamReader reader)
+        {
+            this.reader = reader;
+            Reset ();
+        }
+        
+        public void SetInput (Stream stream)
+        {
+            SetInput (new StreamReader (stream));
+        }
+        
+        public void SetInput (string input)
+        {
+            SetInput (new MemoryStream (Encoding.UTF8.GetBytes (input)));
+        }
+        
+        private void ReadChar ()
+        {
+            peek = (char)reader.Read ();
+            current_column++;
+        }
+        
+        private void UnexpectedCharacter (char ch)
+        {
+            throw new ApplicationException (String.Format ("Unexpected character '{0}' at [{1}:{2}]", 
+                ch, current_line, current_column - 1));
+        }
+        
+        private void InvalidSyntax (string message)
+        {
+            throw new ApplicationException (String.Format ("Invalid syntax: {0} at [{1}:{2}]",
+                message, current_line, current_column));
+        }
+        
+        private StringBuilder GetStringBuilder ()
+        {
+            if (string_buffer == null) {
+                string_buffer = new StringBuilder (64);
+                return string_buffer;
+            }
+            
+            string_buffer.Remove (0, string_buffer.Length);
+            return string_buffer;
+        }
+        
+        private string LexString ()
+        {
+            StringBuilder buffer = GetStringBuilder ();
+            bool read = true;
+            
+            while (!reader.EndOfStream) {
+                if (read) {
+                    ReadChar ();
+                }
+                
+                read = true;
+
+                if (peek == '\\') {
+                    ReadChar ();
+                    switch (peek) {
+                        case 'u':
+                            ReadChar ();
+                            buffer.Append ((char)LexInt (true, 4));
+                            read = false;
+                            break;
+                        case '"':
+                        case '\\':
+                        case '/': buffer.Append (peek); break;
+                        case 'b': buffer.Append ('\b'); break;
+                        case 'f': buffer.Append ('\f'); break;
+                        case 'n': buffer.Append ('\n'); break;
+                        case 'r': buffer.Append ('\r'); break;
+                        case 't': buffer.Append ('\t'); break;
+                        default:
+                            UnexpectedCharacter (peek);
+                            break;
+                    }
+                } else if (peek == '"') {
+                    ReadChar ();
+                    return buffer.ToString ();
+                } else {
+                    buffer.Append (peek);
+                }
+            }
+            
+            if (peek != '"') {
+                InvalidSyntax ("Unterminated string, expected '\"' termination, got '" + peek + "'");
+            } else if (!read && reader.EndOfStream) {
+                ReadChar ();
+            }
+            
+            return buffer.ToString ();
+        }
+        
+        private string LexId ()
+        {
+            StringBuilder buffer = GetStringBuilder ();
+
+            do {
+                buffer.Append (peek);
+                ReadChar ();
+            } while (Char.IsLetterOrDigit (peek));
+
+            return buffer.ToString ();
+        }
+        
+        private double LexInt ()
+        {
+            return LexInt (false, 0);
+        }
+        
+        private double LexInt (bool hex, int maxDigits)
+        {
+            double value = 0.0;
+            int count = 0;
+            
+            do {
+                value = (hex ? 16 : 10) * value +  (hex 
+                    ? peek >= 'A' && peek <= 'F'
+                        ? 10 + peek - 'A'
+                        : (peek >= 'a' && peek <= 'f'
+                            ? 10 + peek - 'a'
+                            : peek - '0')
+                    : peek - '0');
+                    
+                if (maxDigits > 0 && ++count >= maxDigits) {
+                    ReadChar ();
+                    return value;
+                }
+                
+                ReadChar ();
+            } while (Char.IsDigit (peek) || (hex && ((peek >= 'a' && peek <= 'f') || (peek >= 'A' && peek <= 'F'))));
+            
+            return value;
+        }
+        
+        private double LexFraction ()
+        {
+            double fraction = 0;
+            double d = 10;
+            
+            while (true) {
+                ReadChar ();
+
+                if (!Char.IsDigit (peek)) {
+                    break;
+                }
+
+                fraction += (peek - '0') / d;
+                d *= 10;
+            }
+            
+            return fraction;
+        }
+        
+        private double LexNumber ()
+        {
+            double value = 0.0;
+            bool negate = peek == '-';
+            if (negate) {
+                ReadChar ();
+            }
+            
+            if (peek != '0') {
+                value = LexInt ();
+            } else {
+                ReadChar ();
+            }
+            
+            if (peek == '.') {
+                value += LexFraction ();
+            }
+            
+            if (peek == 'e' || peek == 'E') {
+                ReadChar ();
+                if (peek == '-') {
+                    ReadChar ();
+                    value /= Math.Pow (10, LexInt ());
+                } else if (peek == '+') {
+                    ReadChar ();
+                    value *= Math.Pow (10, LexInt ());
+                } else if (Char.IsDigit (peek)) {
+                    value *= Math.Pow (10, LexInt ());
+                } else {
+                    InvalidSyntax ("Malformed exponent");
+                }
+            }
+            
+            if (Char.IsDigit (peek)) {
+                InvalidSyntax ("Numbers starting with 0 must be followed by a . or not " +
+                    "followed by a digit (octal syntax not legal)");
+            }
+            
+            return negate ? -1.0 * value : value;
+        }
+        
+        public Token Scan ()
+        {
+            Token token = InnerScan ();
+            if (token == null) {
+                return null;
+            }
+            
+            token.SourceLine = token_start_line;
+            token.SourceColumn = token_start_column - 1;
+            return token;
+        }
+
+        private Token InnerScan ()
+        {
+            for (; ; ReadChar ()) {
+                if (Char.IsWhiteSpace (peek) && peek != '\n') {
+                    continue;
+                } else if (peek == '\n') {
+                    current_line++;
+                    current_column = 0;
+                } else {
+                    break;
+                }
+            }
+
+            token_start_column = current_column;
+            token_start_line = current_line;
+
+            switch (peek) {
+                case '{': ReadChar (); return new Token (TokenType.ObjectStart);
+                case '}': ReadChar (); return new Token (TokenType.ObjectFinish);
+                case '[': ReadChar (); return new Token (TokenType.ArrayStart);
+                case ']': ReadChar (); return new Token (TokenType.ArrayFinish);
+                case ',': ReadChar (); return new Token (TokenType.Comma);
+                case ':': ReadChar (); return new Token (TokenType.Colon);
+                case '"': return new Token (TokenType.String, LexString ());
+                default:
+                    if (peek == '-' || Char.IsDigit (peek)) {
+                        return new Token (TokenType.Number, LexNumber ());
+                    } else if (Char.IsLetter (peek)) {
+                        string identifier = LexId ();
+                        switch (identifier) {
+                            case "true": return new Token (TokenType.Boolean, true);
+                            case "false": return new Token (TokenType.Boolean, false);
+                            case "null": return new Token (TokenType.Null);
+                            default:
+                                InvalidSyntax ("Invalid identifier '" + identifier + "'");
+                                break;
+                        }
+                    }
+                
+                    if (peek != Char.MaxValue) {
+                        UnexpectedCharacter (peek);
+                    }
+                    break;
+            }
+            
+            return null;
+        }
+    }
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]