From 5b4277577c2fa263032f2838dce86b45d8d3c118 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 24 Oct 2018 22:34:05 +0100 Subject: [PATCH 1/6] (GH-34) Added LitJson --- Source/Cake.Gitter/LitJson/COPYING | 31 + Source/Cake.Gitter/LitJson/IJsonWrapper.cs | 77 ++ Source/Cake.Gitter/LitJson/JsonData.cs | 1103 +++++++++++++++++ Source/Cake.Gitter/LitJson/JsonException.cs | 62 + Source/Cake.Gitter/LitJson/JsonMapper.cs | 1054 ++++++++++++++++ Source/Cake.Gitter/LitJson/JsonMockWrapper.cs | 265 ++++ Source/Cake.Gitter/LitJson/JsonReader.cs | 533 ++++++++ Source/Cake.Gitter/LitJson/JsonWriter.cs | 498 ++++++++ Source/Cake.Gitter/LitJson/Lexer.cs | 983 +++++++++++++++ Source/Cake.Gitter/LitJson/ParserToken.cs | 46 + 10 files changed, 4652 insertions(+) create mode 100644 Source/Cake.Gitter/LitJson/COPYING create mode 100644 Source/Cake.Gitter/LitJson/IJsonWrapper.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonData.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonException.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonMapper.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonMockWrapper.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonReader.cs create mode 100644 Source/Cake.Gitter/LitJson/JsonWriter.cs create mode 100644 Source/Cake.Gitter/LitJson/Lexer.cs create mode 100644 Source/Cake.Gitter/LitJson/ParserToken.cs diff --git a/Source/Cake.Gitter/LitJson/COPYING b/Source/Cake.Gitter/LitJson/COPYING new file mode 100644 index 0000000..44544b8 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/COPYING @@ -0,0 +1,31 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to + +Thank you for reading this notice. Following the tradition of other public +domain projects, here's a blessing: + + May you find forgiveness for yourself and forgive others. + May you experience and share the gift of unconditional love. + May you see light, wherever the illusion of darkness appears. diff --git a/Source/Cake.Gitter/LitJson/IJsonWrapper.cs b/Source/Cake.Gitter/LitJson/IJsonWrapper.cs new file mode 100644 index 0000000..1f03807 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/IJsonWrapper.cs @@ -0,0 +1,77 @@ +#region Header + +/* + * IJsonWrapper.cs + * Interface that represents a type capable of handling all kinds of JSON + * data. This is mainly used when mapping objects through JsonMapper, and + * it's implemented by JsonData. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ + +#endregion Header + +namespace Cake.Gitter +{ + using System.Collections; + using System.Collections.Specialized; + using System; + using System.Collections.Generic; + using System.IO; + + namespace LitJson + { + internal enum JsonType + { + None, + + Object, + Array, + String, + Int, + Long, + Double, + Boolean + } + + internal interface IJsonWrapper : IList, IOrderedDictionary + { + bool IsArray { get; } + bool IsBoolean { get; } + bool IsDouble { get; } + bool IsInt { get; } + bool IsLong { get; } + bool IsObject { get; } + bool IsString { get; } + + bool GetBoolean(); + + double GetDouble(); + + int GetInt(); + + JsonType GetJsonType(); + + long GetLong(); + + string GetString(); + + void SetBoolean(bool val); + + void SetDouble(double val); + + void SetInt(int val); + + void SetJsonType(JsonType type); + + void SetLong(long val); + + void SetString(string val); + + string ToJson(); + + void ToJson(JsonWriter writer); + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonData.cs b/Source/Cake.Gitter/LitJson/JsonData.cs new file mode 100644 index 0000000..d7b77e6 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonData.cs @@ -0,0 +1,1103 @@ +#region Header +/* + * JsonData.cs + * Generic type to hold JSON data (objects, arrays, and so on). This is + * the default type returned by JsonMapper.ToObject(). + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.IO; + + + namespace LitJson + { + internal class JsonData : IJsonWrapper, IEquatable + { + #region Fields + + private IList inst_array; + private bool inst_boolean; + private double inst_double; + private int inst_int; + private long inst_long; + private IDictionary inst_object; + private string inst_string; + private string json; + private JsonType type; + + // Used to implement the IOrderedDictionary interface + private IList> object_list; + + #endregion + + + #region Properties + + public int Count + { + get { return EnsureCollection().Count; } + } + + public bool IsArray + { + get { return type == JsonType.Array; } + } + + public bool IsBoolean + { + get { return type == JsonType.Boolean; } + } + + public bool IsDouble + { + get { return type == JsonType.Double; } + } + + public bool IsInt + { + get { return type == JsonType.Int; } + } + + public bool IsLong + { + get { return type == JsonType.Long; } + } + + public bool IsObject + { + get { return type == JsonType.Object; } + } + + public bool IsString + { + get { return type == JsonType.String; } + } + + public ICollection Keys + { + get + { + EnsureDictionary(); + return inst_object.Keys; + } + } + + #endregion + + + #region ICollection Properties + + int ICollection.Count + { + get { return Count; } + } + + bool ICollection.IsSynchronized + { + get { return EnsureCollection().IsSynchronized; } + } + + object ICollection.SyncRoot + { + get { return EnsureCollection().SyncRoot; } + } + + #endregion + + + #region IDictionary Properties + + bool IDictionary.IsFixedSize + { + get { return EnsureDictionary().IsFixedSize; } + } + + bool IDictionary.IsReadOnly + { + get { return EnsureDictionary().IsReadOnly; } + } + + ICollection IDictionary.Keys + { + get + { + EnsureDictionary(); + IList keys = new List(); + + foreach (KeyValuePair entry in + object_list) + { + keys.Add(entry.Key); + } + + return (ICollection) keys; + } + } + + ICollection IDictionary.Values + { + get + { + EnsureDictionary(); + IList values = new List(); + + foreach (KeyValuePair entry in + object_list) + { + values.Add(entry.Value); + } + + return (ICollection) values; + } + } + + #endregion + + + + #region IJsonWrapper Properties + + bool IJsonWrapper.IsArray + { + get { return IsArray; } + } + + bool IJsonWrapper.IsBoolean + { + get { return IsBoolean; } + } + + bool IJsonWrapper.IsDouble + { + get { return IsDouble; } + } + + bool IJsonWrapper.IsInt + { + get { return IsInt; } + } + + bool IJsonWrapper.IsLong + { + get { return IsLong; } + } + + bool IJsonWrapper.IsObject + { + get { return IsObject; } + } + + bool IJsonWrapper.IsString + { + get { return IsString; } + } + + #endregion + + + #region IList Properties + + bool IList.IsFixedSize + { + get { return EnsureList().IsFixedSize; } + } + + bool IList.IsReadOnly + { + get { return EnsureList().IsReadOnly; } + } + + #endregion + + + #region IDictionary Indexer + + object IDictionary.this[object key] + { + get { return EnsureDictionary()[key]; } + + set + { + if (!(key is String)) + throw new ArgumentException( + "The key has to be a string"); + + JsonData data = ToJsonData(value); + + this[(string) key] = data; + } + } + + #endregion + + + #region IOrderedDictionary Indexer + + object IOrderedDictionary.this[int idx] + { + get + { + EnsureDictionary(); + return object_list[idx].Value; + } + + set + { + EnsureDictionary(); + JsonData data = ToJsonData(value); + + KeyValuePair old_entry = object_list[idx]; + + inst_object[old_entry.Key] = data; + + KeyValuePair entry = + new KeyValuePair(old_entry.Key, data); + + object_list[idx] = entry; + } + } + + #endregion + + + #region IList Indexer + + object IList.this[int index] + { + get { return EnsureList()[index]; } + + set + { + EnsureList(); + JsonData data = ToJsonData(value); + + this[index] = data; + } + } + + #endregion + + + #region Public Indexers + + public JsonData this[string prop_name] + { + get + { + EnsureDictionary(); + return inst_object[prop_name]; + } + + set + { + EnsureDictionary(); + + KeyValuePair entry = + new KeyValuePair(prop_name, value); + + if (inst_object.ContainsKey(prop_name)) + { + for (int i = 0; i < object_list.Count; i++) + { + if (object_list[i].Key == prop_name) + { + object_list[i] = entry; + break; + } + } + } + else + object_list.Add(entry); + + inst_object[prop_name] = value; + + json = null; + } + } + + public JsonData this[int index] + { + get + { + EnsureCollection(); + + if (type == JsonType.Array) + return inst_array[index]; + + return object_list[index].Value; + } + + set + { + EnsureCollection(); + + if (type == JsonType.Array) + inst_array[index] = value; + else + { + KeyValuePair entry = object_list[index]; + KeyValuePair new_entry = + new KeyValuePair(entry.Key, value); + + object_list[index] = new_entry; + inst_object[entry.Key] = value; + } + + json = null; + } + } + + #endregion + + + #region Constructors + + public JsonData() + { + } + + public JsonData(bool boolean) + { + type = JsonType.Boolean; + inst_boolean = boolean; + } + + public JsonData(double number) + { + type = JsonType.Double; + inst_double = number; + } + + public JsonData(int number) + { + type = JsonType.Int; + inst_int = number; + } + + public JsonData(long number) + { + type = JsonType.Long; + inst_long = number; + } + + public JsonData(object obj) + { + if (obj is Boolean) + { + type = JsonType.Boolean; + inst_boolean = (bool) obj; + return; + } + + if (obj is Double) + { + type = JsonType.Double; + inst_double = (double) obj; + return; + } + + if (obj is Int32) + { + type = JsonType.Int; + inst_int = (int) obj; + return; + } + + if (obj is Int64) + { + type = JsonType.Long; + inst_long = (long) obj; + return; + } + + if (obj is String) + { + type = JsonType.String; + inst_string = (string) obj; + return; + } + + throw new ArgumentException( + "Unable to wrap the given object with JsonData"); + } + + public JsonData(string str) + { + type = JsonType.String; + inst_string = str; + } + + #endregion + + + #region Implicit Conversions + + public static implicit operator JsonData(Boolean data) + { + return new JsonData(data); + } + + public static implicit operator JsonData(Double data) + { + return new JsonData(data); + } + + public static implicit operator JsonData(Int32 data) + { + return new JsonData(data); + } + + public static implicit operator JsonData(Int64 data) + { + return new JsonData(data); + } + + public static implicit operator JsonData(String data) + { + return new JsonData(data); + } + + #endregion + + + #region Explicit Conversions + + public static explicit operator Boolean(JsonData data) + { + if (data.type != JsonType.Boolean) + throw new InvalidCastException( + "Instance of JsonData doesn't hold a double"); + + return data.inst_boolean; + } + + public static explicit operator Double(JsonData data) + { + if (data.type != JsonType.Double) + throw new InvalidCastException( + "Instance of JsonData doesn't hold a double"); + + return data.inst_double; + } + + public static explicit operator Int32(JsonData data) + { + if (data.type != JsonType.Int) + throw new InvalidCastException( + "Instance of JsonData doesn't hold an int"); + + return data.inst_int; + } + + public static explicit operator Int64(JsonData data) + { + if (data.type != JsonType.Long) + throw new InvalidCastException( + "Instance of JsonData doesn't hold an int"); + + return data.inst_long; + } + + public static explicit operator String(JsonData data) + { + if (data.type != JsonType.String) + throw new InvalidCastException( + "Instance of JsonData doesn't hold a string"); + + return data.inst_string; + } + + #endregion + + + #region ICollection Methods + + void ICollection.CopyTo(Array array, int index) + { + EnsureCollection().CopyTo(array, index); + } + + #endregion + + + #region IDictionary Methods + + void IDictionary.Add(object key, object value) + { + JsonData data = ToJsonData(value); + + EnsureDictionary().Add(key, data); + + KeyValuePair entry = + new KeyValuePair((string) key, data); + object_list.Add(entry); + + json = null; + } + + void IDictionary.Clear() + { + EnsureDictionary().Clear(); + object_list.Clear(); + json = null; + } + + bool IDictionary.Contains(object key) + { + return EnsureDictionary().Contains(key); + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return ((IOrderedDictionary) this).GetEnumerator(); + } + + void IDictionary.Remove(object key) + { + EnsureDictionary().Remove(key); + + for (int i = 0; i < object_list.Count; i++) + { + if (object_list[i].Key == (string) key) + { + object_list.RemoveAt(i); + break; + } + } + + json = null; + } + + #endregion + + + #region IEnumerable Methods + + IEnumerator IEnumerable.GetEnumerator() + { + return EnsureCollection().GetEnumerator(); + } + + #endregion + + + #region IJsonWrapper Methods + + bool IJsonWrapper.GetBoolean() + { + if (type != JsonType.Boolean) + throw new InvalidOperationException( + "JsonData instance doesn't hold a boolean"); + + return inst_boolean; + } + + double IJsonWrapper.GetDouble() + { + if (type != JsonType.Double) + throw new InvalidOperationException( + "JsonData instance doesn't hold a double"); + + return inst_double; + } + + int IJsonWrapper.GetInt() + { + if (type != JsonType.Int) + throw new InvalidOperationException( + "JsonData instance doesn't hold an int"); + + return inst_int; + } + + long IJsonWrapper.GetLong() + { + if (type != JsonType.Long) + throw new InvalidOperationException( + "JsonData instance doesn't hold a long"); + + return inst_long; + } + + string IJsonWrapper.GetString() + { + if (type != JsonType.String) + throw new InvalidOperationException( + "JsonData instance doesn't hold a string"); + + return inst_string; + } + + void IJsonWrapper.SetBoolean(bool val) + { + type = JsonType.Boolean; + inst_boolean = val; + json = null; + } + + void IJsonWrapper.SetDouble(double val) + { + type = JsonType.Double; + inst_double = val; + json = null; + } + + void IJsonWrapper.SetInt(int val) + { + type = JsonType.Int; + inst_int = val; + json = null; + } + + void IJsonWrapper.SetLong(long val) + { + type = JsonType.Long; + inst_long = val; + json = null; + } + + void IJsonWrapper.SetString(string val) + { + type = JsonType.String; + inst_string = val; + json = null; + } + + string IJsonWrapper.ToJson() + { + return ToJson(); + } + + void IJsonWrapper.ToJson(JsonWriter writer) + { + ToJson(writer); + } + + #endregion + + + #region IList Methods + + int IList.Add(object value) + { + return Add(value); + } + + void IList.Clear() + { + EnsureList().Clear(); + json = null; + } + + bool IList.Contains(object value) + { + return EnsureList().Contains(value); + } + + int IList.IndexOf(object value) + { + return EnsureList().IndexOf(value); + } + + void IList.Insert(int index, object value) + { + EnsureList().Insert(index, value); + json = null; + } + + void IList.Remove(object value) + { + EnsureList().Remove(value); + json = null; + } + + void IList.RemoveAt(int index) + { + EnsureList().RemoveAt(index); + json = null; + } + + #endregion + + + #region IOrderedDictionary Methods + + IDictionaryEnumerator IOrderedDictionary.GetEnumerator() + { + EnsureDictionary(); + + return new OrderedDictionaryEnumerator( + object_list.GetEnumerator()); + } + + void IOrderedDictionary.Insert(int idx, object key, object value) + { + string property = (string) key; + JsonData data = ToJsonData(value); + + this[property] = data; + + KeyValuePair entry = + new KeyValuePair(property, data); + + object_list.Insert(idx, entry); + } + + void IOrderedDictionary.RemoveAt(int idx) + { + EnsureDictionary(); + + inst_object.Remove(object_list[idx].Key); + object_list.RemoveAt(idx); + } + + #endregion + + + #region Private Methods + + private ICollection EnsureCollection() + { + if (type == JsonType.Array) + return (ICollection) inst_array; + + if (type == JsonType.Object) + return (ICollection) inst_object; + + throw new InvalidOperationException( + "The JsonData instance has to be initialized first"); + } + + private IDictionary EnsureDictionary() + { + if (type == JsonType.Object) + return (IDictionary) inst_object; + + if (type != JsonType.None) + throw new InvalidOperationException( + "Instance of JsonData is not a dictionary"); + + type = JsonType.Object; + inst_object = new Dictionary(); + object_list = new List>(); + + return (IDictionary) inst_object; + } + + private IList EnsureList() + { + if (type == JsonType.Array) + return (IList) inst_array; + + if (type != JsonType.None) + throw new InvalidOperationException( + "Instance of JsonData is not a list"); + + type = JsonType.Array; + inst_array = new List(); + + return (IList) inst_array; + } + + private JsonData ToJsonData(object obj) + { + if (obj == null) + return null; + + if (obj is JsonData) + return (JsonData) obj; + + return new JsonData(obj); + } + + private static void WriteJson(IJsonWrapper obj, JsonWriter writer) + { + if (obj == null) + { + writer.Write(null); + return; + } + + if (obj.IsString) + { + writer.Write(obj.GetString()); + return; + } + + if (obj.IsBoolean) + { + writer.Write(obj.GetBoolean()); + return; + } + + if (obj.IsDouble) + { + writer.Write(obj.GetDouble()); + return; + } + + if (obj.IsInt) + { + writer.Write(obj.GetInt()); + return; + } + + if (obj.IsLong) + { + writer.Write(obj.GetLong()); + return; + } + + if (obj.IsArray) + { + writer.WriteArrayStart(); + foreach (object elem in (IList) obj) + WriteJson((JsonData) elem, writer); + writer.WriteArrayEnd(); + + return; + } + + if (obj.IsObject) + { + writer.WriteObjectStart(); + + foreach (DictionaryEntry entry in ((IDictionary) obj)) + { + writer.WritePropertyName((string) entry.Key); + WriteJson((JsonData) entry.Value, writer); + } + writer.WriteObjectEnd(); + + return; + } + } + + #endregion + + + public int Add(object value) + { + JsonData data = ToJsonData(value); + + json = null; + + return EnsureList().Add(data); + } + + public void Clear() + { + if (IsObject) + { + ((IDictionary) this).Clear(); + return; + } + + if (IsArray) + { + ((IList) this).Clear(); + return; + } + } + + public bool Equals(JsonData x) + { + if (x == null) + return false; + + if (x.type != this.type) + return false; + + switch (this.type) + { + case JsonType.None: + return true; + + case JsonType.Object: + return this.inst_object.Equals(x.inst_object); + + case JsonType.Array: + return this.inst_array.Equals(x.inst_array); + + case JsonType.String: + return this.inst_string.Equals(x.inst_string); + + case JsonType.Int: + return this.inst_int.Equals(x.inst_int); + + case JsonType.Long: + return this.inst_long.Equals(x.inst_long); + + case JsonType.Double: + return this.inst_double.Equals(x.inst_double); + + case JsonType.Boolean: + return this.inst_boolean.Equals(x.inst_boolean); + } + + return false; + } + + public JsonType GetJsonType() + { + return type; + } + + public void SetJsonType(JsonType type) + { + if (this.type == type) + return; + + switch (type) + { + case JsonType.None: + break; + + case JsonType.Object: + inst_object = new Dictionary(); + object_list = new List>(); + break; + + case JsonType.Array: + inst_array = new List(); + break; + + case JsonType.String: + inst_string = default(String); + break; + + case JsonType.Int: + inst_int = default(Int32); + break; + + case JsonType.Long: + inst_long = default(Int64); + break; + + case JsonType.Double: + inst_double = default(Double); + break; + + case JsonType.Boolean: + inst_boolean = default(Boolean); + break; + } + + this.type = type; + } + + public string ToJson() + { + if (json != null) + return json; + + StringWriter sw = new StringWriter(); + JsonWriter writer = new JsonWriter(sw); + writer.Validate = false; + + WriteJson(this, writer); + json = sw.ToString(); + + return json; + } + + public void ToJson(JsonWriter writer) + { + bool old_validate = writer.Validate; + + writer.Validate = false; + + WriteJson(this, writer); + + writer.Validate = old_validate; + } + + public override string ToString() + { + switch (type) + { + case JsonType.Array: + return "JsonData array"; + + case JsonType.Boolean: + return inst_boolean.ToString(); + + case JsonType.Double: + return inst_double.ToString(); + + case JsonType.Int: + return inst_int.ToString(); + + case JsonType.Long: + return inst_long.ToString(); + + case JsonType.Object: + return "JsonData object"; + + case JsonType.String: + return inst_string; + } + + return "Uninitialized JsonData"; + } + } + + + internal class OrderedDictionaryEnumerator : IDictionaryEnumerator + { + IEnumerator> list_enumerator; + + + public object Current + { + get { return Entry; } + } + + public DictionaryEntry Entry + { + get + { + KeyValuePair curr = list_enumerator.Current; + return new DictionaryEntry(curr.Key, curr.Value); + } + } + + public object Key + { + get { return list_enumerator.Current.Key; } + } + + public object Value + { + get { return list_enumerator.Current.Value; } + } + + + public OrderedDictionaryEnumerator( + IEnumerator> enumerator) + { + list_enumerator = enumerator; + } + + + public bool MoveNext() + { + return list_enumerator.MoveNext(); + } + + public void Reset() + { + list_enumerator.Reset(); + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonException.cs b/Source/Cake.Gitter/LitJson/JsonException.cs new file mode 100644 index 0000000..0a13b88 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonException.cs @@ -0,0 +1,62 @@ +#region Header +/* + * JsonException.cs + * Base class throwed by LitJSON when a parsing error occurs. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + + + namespace LitJson + { + internal class JsonException : Exception + { + public JsonException() : base() + { + } + + internal JsonException(ParserToken token) : + base(String.Format( + "Invalid token '{0}' in input string", token)) + { + } + + internal JsonException(ParserToken token, + Exception inner_exception) : + base(String.Format( + "Invalid token '{0}' in input string", token), + inner_exception) + { + } + + internal JsonException(int c) : + base(String.Format( + "Invalid character '{0}' in input string", (char) c)) + { + } + + internal JsonException(int c, Exception inner_exception) : + base(String.Format( + "Invalid character '{0}' in input string", (char) c), + inner_exception) + { + } + + + public JsonException(string message) : base(message) + { + } + + public JsonException(string message, Exception inner_exception) : + base(message, inner_exception) + { + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonMapper.cs b/Source/Cake.Gitter/LitJson/JsonMapper.cs new file mode 100644 index 0000000..3ea306c --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonMapper.cs @@ -0,0 +1,1054 @@ +#region Header +/* + * JsonMapper.cs + * JSON to .Net object and object to JSON conversions. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Reflection; + + + namespace LitJson + { + internal class JsonName : Attribute + { + public string Name { get; protected set; } + + public JsonName(string name) + { + this.Name = name; + } + } + + + internal struct PropertyMetadata + { + public MemberInfo Info; + public bool IsField; + public Type Type; + } + + + internal struct ArrayMetadata + { + private Type element_type; + private bool is_array; + private bool is_list; + + + public Type ElementType + { + get + { + if (element_type == null) + return typeof(JsonData); + + return element_type; + } + + set { element_type = value; } + } + + public bool IsArray + { + get { return is_array; } + set { is_array = value; } + } + + public bool IsList + { + get { return is_list; } + set { is_list = value; } + } + } + + + internal struct ObjectMetadata + { + private Type element_type; + private bool is_dictionary; + + private IDictionary properties; + + + public Type ElementType + { + get + { + if (element_type == null) + return typeof(JsonData); + + return element_type; + } + + set { element_type = value; } + } + + public bool IsDictionary + { + get { return is_dictionary; } + set { is_dictionary = value; } + } + + public IDictionary Properties + { + get { return properties; } + set { properties = value; } + } + } + + + internal delegate void ExporterFunc(object obj, JsonWriter writer); + + internal delegate void ExporterFunc(T obj, JsonWriter writer); + + internal delegate object ImporterFunc(object input); + + internal delegate TValue ImporterFunc(TJson input); + + internal delegate IJsonWrapper WrapperFactory(); + + + internal class JsonMapper + { + #region Fields + + private static readonly int max_nesting_depth; + + private static readonly IFormatProvider datetime_format; + + private static readonly IDictionary base_exporters_table; + private static readonly IDictionary custom_exporters_table; + + private static readonly IDictionary> base_importers_table; + + private static readonly IDictionary> custom_importers_table; + + private static readonly IDictionary array_metadata; + private static readonly object array_metadata_lock = new Object(); + + private static readonly IDictionary> conv_ops; + + private static readonly object conv_ops_lock = new Object(); + + private static readonly IDictionary object_metadata; + private static readonly object object_metadata_lock = new Object(); + + private static readonly IDictionary> type_properties; + + private static readonly object type_properties_lock = new Object(); + + private static readonly JsonWriter static_writer; + private static readonly object static_writer_lock = new Object(); + + #endregion + + + #region Constructors + + static JsonMapper() + { + max_nesting_depth = 100; + + array_metadata = new Dictionary(); + conv_ops = new Dictionary>(); + object_metadata = new Dictionary(); + type_properties = new Dictionary>(); + + static_writer = new JsonWriter(); + + datetime_format = DateTimeFormatInfo.InvariantInfo; + + base_exporters_table = new Dictionary(); + custom_exporters_table = new Dictionary(); + + base_importers_table = new Dictionary>(); + custom_importers_table = new Dictionary>(); + + RegisterBaseExporters(); + RegisterBaseImporters(); + } + + #endregion + + + #region Private Methods + + private static void AddArrayMetadata(Type type) + { + if (array_metadata.ContainsKey(type)) + return; + + ArrayMetadata data = new ArrayMetadata(); + + data.IsArray = type.IsArray; + + if (type.GetTypeInfo().GetInterface("System.Collections.IList") != null) + data.IsList = true; + + foreach (PropertyInfo p_info in type.GetProperties()) + { + if (p_info.Name != "Item") + continue; + + ParameterInfo[] parameters = p_info.GetIndexParameters(); + + if (parameters.Length != 1) + continue; + + if (parameters[0].ParameterType == typeof(int)) + data.ElementType = p_info.PropertyType; + } + + lock (array_metadata_lock) + { + try + { + array_metadata.Add(type, data); + } + catch (ArgumentException) + { + return; + } + } + } + + private static void AddObjectMetadata(Type type) + { + if (object_metadata.ContainsKey(type)) + return; + + ObjectMetadata data = new ObjectMetadata(); + + if (type.GetTypeInfo().GetInterface("System.Collections.IDictionary") != null) + data.IsDictionary = true; + + data.Properties = new Dictionary(); + + foreach (PropertyInfo p_info in type.GetProperties()) + { + if (p_info.Name == "Item") + { + ParameterInfo[] parameters = p_info.GetIndexParameters(); + + if (parameters.Length != 1) + continue; + + if (parameters[0].ParameterType == typeof(string)) + data.ElementType = p_info.PropertyType; + + continue; + } + + PropertyMetadata p_data = new PropertyMetadata(); + p_data.Info = p_info; + p_data.Type = p_info.PropertyType; + + data.Properties.Add(GetPropertyName(p_info), p_data); + } + + foreach (FieldInfo f_info in type.GetFields()) + { + PropertyMetadata p_data = new PropertyMetadata(); + p_data.Info = f_info; + p_data.IsField = true; + p_data.Type = f_info.FieldType; + + data.Properties.Add(GetPropertyName(f_info), p_data); + } + + lock (object_metadata_lock) + { + try + { + object_metadata.Add(type, data); + } + catch (ArgumentException) + { + return; + } + } + } + + private static void AddTypeProperties(Type type) + { + if (type_properties.ContainsKey(type)) + return; + + IList props = new List(); + + foreach (PropertyInfo p_info in type.GetProperties()) + { + if (p_info.Name == "Item") + continue; + + PropertyMetadata p_data = new PropertyMetadata(); + p_data.Info = p_info; + p_data.IsField = false; + props.Add(p_data); + } + + foreach (FieldInfo f_info in type.GetFields()) + { + PropertyMetadata p_data = new PropertyMetadata(); + p_data.Info = f_info; + p_data.IsField = true; + + props.Add(p_data); + } + + lock (type_properties_lock) + { + try + { + type_properties.Add(type, props); + } + catch (ArgumentException) + { + return; + } + } + } + + private static MethodInfo GetConvOp(Type t1, Type t2) + { + lock (conv_ops_lock) + { + if (!conv_ops.ContainsKey(t1)) + conv_ops.Add(t1, new Dictionary()); + } + + if (conv_ops[t1].ContainsKey(t2)) + return conv_ops[t1][t2]; + + MethodInfo op = t1.GetMethod( + "op_Implicit", new Type[] {t2}); + + lock (conv_ops_lock) + { + try + { + conv_ops[t1].Add(t2, op); + } + catch (ArgumentException) + { + return conv_ops[t1][t2]; + } + } + + return op; + } + + private static object ReadValue(Type inst_type, JsonReader reader) + { + reader.Read(); + + if (reader.Token == JsonToken.ArrayEnd) + return null; + + Type underlying_type = Nullable.GetUnderlyingType(inst_type); + Type value_type = underlying_type ?? inst_type; + + if (reader.Token == JsonToken.Null) + { + if (inst_type.GetTypeInfo().IsClass || underlying_type != null) + { + return null; + } + + throw new JsonException(String.Format( + "Can't assign null to an instance of type {0}", + inst_type)); + } + + if (reader.Token == JsonToken.Double || + reader.Token == JsonToken.Int || + reader.Token == JsonToken.Long || + reader.Token == JsonToken.String || + reader.Token == JsonToken.Boolean) + { + + Type json_type = reader.Value.GetType(); + + if (value_type.IsAssignableFrom(json_type)) + return reader.Value; + + // If there's a custom importer that fits, use it + if (custom_importers_table.ContainsKey(json_type) && + custom_importers_table[json_type].ContainsKey( + value_type)) + { + + ImporterFunc importer = + custom_importers_table[json_type][value_type]; + + return importer(reader.Value); + } + + // Maybe there's a base importer that works + if (base_importers_table.ContainsKey(json_type) && + base_importers_table[json_type].ContainsKey( + value_type)) + { + + ImporterFunc importer = + base_importers_table[json_type][value_type]; + + return importer(reader.Value); + } + + // Maybe it's an enum + if (value_type.GetTypeInfo().IsEnum) + return Enum.ToObject(value_type, reader.Value); + + // Try using an implicit conversion operator + MethodInfo conv_op = GetConvOp(value_type, json_type); + + if (conv_op != null) + return conv_op.Invoke(null, + new object[] {reader.Value}); + + // No luck + throw new JsonException(String.Format( + "Can't assign value '{0}' (type {1}) to type {2}", + reader.Value, json_type, inst_type)); + } + + object instance = null; + + if (reader.Token == JsonToken.ArrayStart) + { + + AddArrayMetadata(inst_type); + ArrayMetadata t_data = array_metadata[inst_type]; + + if (!t_data.IsArray && !t_data.IsList) + throw new JsonException(String.Format( + "Type {0} can't act as an array", + inst_type)); + + IList list; + Type elem_type; + + if (!t_data.IsArray) + { + list = (IList) Activator.CreateInstance(inst_type); + elem_type = t_data.ElementType; + } + else + { + list = new ArrayList(); + elem_type = inst_type.GetElementType(); + } + + while (true) + { + object item = ReadValue(elem_type, reader); + if (item == null && reader.Token == JsonToken.ArrayEnd) + break; + + list.Add(item); + } + + if (t_data.IsArray) + { + int n = list.Count; + instance = Array.CreateInstance(elem_type, n); + + for (int i = 0; i < n; i++) + ((Array) instance).SetValue(list[i], i); + } + else + instance = list; + + } + else if (reader.Token == JsonToken.ObjectStart) + { + AddObjectMetadata(value_type); + ObjectMetadata t_data = object_metadata[value_type]; + + instance = Activator.CreateInstance(value_type); + + while (true) + { + reader.Read(); + + if (reader.Token == JsonToken.ObjectEnd) + break; + + string property = (string) reader.Value; + + if (t_data.Properties.ContainsKey(property)) + { + PropertyMetadata prop_data = + t_data.Properties[property]; + + if (prop_data.IsField) + { + ((FieldInfo) prop_data.Info).SetValue( + instance, ReadValue(prop_data.Type, reader)); + } + else + { + PropertyInfo p_info = + (PropertyInfo) prop_data.Info; + + if (p_info.CanWrite) + p_info.SetValue( + instance, + ReadValue(prop_data.Type, reader), + null); + else + ReadValue(prop_data.Type, reader); + } + + } + else + { + if (!t_data.IsDictionary) + { + + if (!reader.SkipNonMembers) + { + throw new JsonException(String.Format( + "The type {0} doesn't have the " + + "property '{1}'", + inst_type, property)); + } + else + { + ReadSkip(reader); + continue; + } + } + + ((IDictionary) instance).Add( + property, ReadValue( + t_data.ElementType, reader)); + } + + } + + } + + return instance; + } + + private static IJsonWrapper ReadValue(WrapperFactory factory, + JsonReader reader) + { + reader.Read(); + + if (reader.Token == JsonToken.ArrayEnd || + reader.Token == JsonToken.Null) + return null; + + IJsonWrapper instance = factory(); + + if (reader.Token == JsonToken.String) + { + instance.SetString((string) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Double) + { + instance.SetDouble((double) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Int) + { + instance.SetInt((int) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Long) + { + instance.SetLong((long) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Boolean) + { + instance.SetBoolean((bool) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.ArrayStart) + { + instance.SetJsonType(JsonType.Array); + + while (true) + { + IJsonWrapper item = ReadValue(factory, reader); + if (item == null && reader.Token == JsonToken.ArrayEnd) + break; + + ((IList) instance).Add(item); + } + } + else if (reader.Token == JsonToken.ObjectStart) + { + instance.SetJsonType(JsonType.Object); + + while (true) + { + reader.Read(); + + if (reader.Token == JsonToken.ObjectEnd) + break; + + string property = (string) reader.Value; + + ((IDictionary) instance)[property] = ReadValue( + factory, reader); + } + + } + + return instance; + } + + private static void ReadSkip(JsonReader reader) + { + ToWrapper( + delegate { return new JsonMockWrapper(); }, reader); + } + + private static void RegisterBaseExporters() + { + base_exporters_table[typeof(byte)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToInt32((byte) obj)); + }; + + base_exporters_table[typeof(char)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToString((char) obj)); + }; + + base_exporters_table[typeof(DateTime)] = + delegate(object obj, JsonWriter writer) + { + writer.Write(Convert.ToString((DateTime) obj, + datetime_format)); + }; + + base_exporters_table[typeof(decimal)] = + delegate(object obj, JsonWriter writer) { + writer.Write((decimal) obj); + }; + + base_exporters_table[typeof(sbyte)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToInt32((sbyte) obj)); + }; + + base_exporters_table[typeof(short)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToInt32((short) obj)); + }; + + base_exporters_table[typeof(ushort)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToInt32((ushort) obj)); + }; + + base_exporters_table[typeof(uint)] = + delegate(object obj, JsonWriter writer) { + writer.Write(Convert.ToUInt64((uint) obj)); + }; + + base_exporters_table[typeof(ulong)] = + delegate(object obj, JsonWriter writer) { + writer.Write((ulong) obj); + }; + } + + private static void RegisterBaseImporters() + { + ImporterFunc importer; + + importer = delegate(object input) { + return Convert.ToByte((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(byte), importer); + + importer = delegate(object input) { + return Convert.ToUInt64((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(ulong), importer); + + importer = delegate(object input) { + return Convert.ToSByte((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(sbyte), importer); + + importer = delegate(object input) { + return Convert.ToInt16((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(short), importer); + + importer = delegate(object input) { + return Convert.ToUInt16((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(ushort), importer); + + importer = delegate(object input) { + return Convert.ToUInt32((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(uint), importer); + + importer = delegate(object input) { + return Convert.ToSingle((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(float), importer); + + importer = delegate(object input) { + return Convert.ToDouble((int) input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(double), importer); + + importer = delegate(object input) { + return Convert.ToDecimal((double) input); + }; + RegisterImporter(base_importers_table, typeof(double), + typeof(decimal), importer); + + + importer = delegate(object input) { + return Convert.ToUInt32((long) input); + }; + RegisterImporter(base_importers_table, typeof(long), + typeof(uint), importer); + + importer = delegate(object input) { + return Convert.ToChar((string) input); + }; + RegisterImporter(base_importers_table, typeof(string), + typeof(char), importer); + + importer = delegate(object input) { + return Convert.ToDateTime((string) input, datetime_format); + }; + RegisterImporter(base_importers_table, typeof(string), + typeof(DateTime), importer); + } + + private static void RegisterImporter( + IDictionary> table, + Type json_type, Type value_type, ImporterFunc importer) + { + if (!table.ContainsKey(json_type)) + table.Add(json_type, new Dictionary()); + + table[json_type][value_type] = importer; + } + + private static void WriteValue(object obj, JsonWriter writer, + bool writer_is_private, + int depth) + { + if (depth > max_nesting_depth) + throw new JsonException( + String.Format("Max allowed object depth reached while " + + "trying to export from type {0}", + obj.GetType())); + + if (obj == null) + { + writer.Write(null); + return; + } + + if (obj is IJsonWrapper) + { + if (writer_is_private) + writer.TextWriter.Write(((IJsonWrapper) obj).ToJson()); + else + ((IJsonWrapper) obj).ToJson(writer); + + return; + } + + if (obj is String) + { + writer.Write((string) obj); + return; + } + + if (obj is Double) + { + writer.Write((double) obj); + return; + } + + if (obj is Int32) + { + writer.Write((int) obj); + return; + } + + if (obj is Boolean) + { + writer.Write((bool) obj); + return; + } + + if (obj is Int64) + { + writer.Write((long) obj); + return; + } + + if (obj is Array) + { + writer.WriteArrayStart(); + + foreach (object elem in (Array) obj) + WriteValue(elem, writer, writer_is_private, depth + 1); + + writer.WriteArrayEnd(); + + return; + } + + if (obj is IList) + { + writer.WriteArrayStart(); + foreach (object elem in (IList) obj) + WriteValue(elem, writer, writer_is_private, depth + 1); + writer.WriteArrayEnd(); + + return; + } + + if (obj is IDictionary) + { + writer.WriteObjectStart(); + foreach (DictionaryEntry entry in (IDictionary) obj) + { + if (entry.Value == null) continue; + + writer.WritePropertyName((string) entry.Key); + WriteValue(entry.Value, writer, writer_is_private, + depth + 1); + } + writer.WriteObjectEnd(); + + return; + } + + Type obj_type = obj.GetType(); + + // See if there's a custom exporter for the object + if (custom_exporters_table.ContainsKey(obj_type)) + { + ExporterFunc exporter = custom_exporters_table[obj_type]; + exporter(obj, writer); + + return; + } + + // If not, maybe there's a base exporter + if (base_exporters_table.ContainsKey(obj_type)) + { + ExporterFunc exporter = base_exporters_table[obj_type]; + exporter(obj, writer); + + return; + } + + // Last option, let's see if it's an enum + if (obj is Enum) + { + Type e_type = Enum.GetUnderlyingType(obj_type); + + if (e_type == typeof(long) + || e_type == typeof(uint) + || e_type == typeof(ulong)) + writer.Write((ulong) obj); + else + writer.Write((int) obj); + + return; + } + + // Okay, so it looks like the input should be exported as an + // object + AddTypeProperties(obj_type); + IList props = type_properties[obj_type]; + + writer.WriteObjectStart(); + foreach (PropertyMetadata p_data in props) + { + string propertyName = null; + object propertyValue = null; + if (p_data.IsField) + { + propertyName = p_data.Info.Name; + propertyValue = ((FieldInfo) p_data.Info).GetValue(obj); + } + else + { + PropertyInfo p_info = (PropertyInfo) p_data.Info; + + if (p_info.CanRead) + { + propertyName = GetPropertyName(p_data.Info); + propertyValue = p_info.GetValue(obj, null); + } + } + + if (propertyName == null || propertyValue == null) continue; + + writer.WritePropertyName(propertyName); + WriteValue(propertyValue, + writer, writer_is_private, depth + 1); + } + writer.WriteObjectEnd(); + } + + private static string GetPropertyName(MemberInfo memInfo) + { + var attrs = memInfo.GetCustomAttributes(typeof(JsonName), true); + if (attrs.Any()) + return ((JsonName) attrs.ToArray()[0]).Name; + else + return memInfo.Name; + } + + #endregion + + + public static string ToJson(object obj) + { + lock (static_writer_lock) + { + static_writer.Reset(); + + WriteValue(obj, static_writer, true, 0); + + return static_writer.ToString(); + } + } + + public static void ToJson(object obj, JsonWriter writer) + { + WriteValue(obj, writer, false, 0); + } + + public static JsonData ToObject(JsonReader reader) + { + return (JsonData) ToWrapper( + delegate { return new JsonData(); }, reader); + } + + public static JsonData ToObject(TextReader reader) + { + JsonReader json_reader = new JsonReader(reader); + + return (JsonData) ToWrapper( + delegate { return new JsonData(); }, json_reader); + } + + public static JsonData ToObject(string json) + { + return (JsonData) ToWrapper( + delegate { return new JsonData(); }, json); + } + + public static T ToObject(JsonReader reader) + { + return (T) ReadValue(typeof(T), reader); + } + + public static T ToObject(TextReader reader) + { + JsonReader json_reader = new JsonReader(reader); + + return (T) ReadValue(typeof(T), json_reader); + } + + public static T ToObject(string json) + { + JsonReader reader = new JsonReader(json); + + return (T) ReadValue(typeof(T), reader); + } + + public static IJsonWrapper ToWrapper(WrapperFactory factory, + JsonReader reader) + { + return ReadValue(factory, reader); + } + + public static IJsonWrapper ToWrapper(WrapperFactory factory, + string json) + { + JsonReader reader = new JsonReader(json); + + return ReadValue(factory, reader); + } + + public static void RegisterExporter(ExporterFunc exporter) + { + ExporterFunc exporter_wrapper = + delegate(object obj, JsonWriter writer) { + exporter((T) obj, writer); + }; + + custom_exporters_table[typeof(T)] = exporter_wrapper; + } + + public static void RegisterImporter( + ImporterFunc importer) + { + ImporterFunc importer_wrapper = + delegate(object input) { + return importer((TJson) input); + }; + + RegisterImporter(custom_importers_table, typeof(TJson), + typeof(TValue), importer_wrapper); + } + + public static void UnregisterExporters() + { + custom_exporters_table.Clear(); + } + + public static void UnregisterImporters() + { + custom_importers_table.Clear(); + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonMockWrapper.cs b/Source/Cake.Gitter/LitJson/JsonMockWrapper.cs new file mode 100644 index 0000000..0d6d470 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonMockWrapper.cs @@ -0,0 +1,265 @@ +#region Header +/* + * JsonMockWrapper.cs + * Mock object implementing IJsonWrapper, to facilitate actions like + * skipping data more efficiently. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections; + using System.Collections.Specialized; + + + namespace LitJson + { + internal class JsonMockWrapper : IJsonWrapper + { + public bool IsArray + { + get { return false; } + } + + public bool IsBoolean + { + get { return false; } + } + + public bool IsDouble + { + get { return false; } + } + + public bool IsInt + { + get { return false; } + } + + public bool IsLong + { + get { return false; } + } + + public bool IsObject + { + get { return false; } + } + + public bool IsString + { + get { return false; } + } + + public bool GetBoolean() + { + return false; + } + + public double GetDouble() + { + return 0.0; + } + + public int GetInt() + { + return 0; + } + + public JsonType GetJsonType() + { + return JsonType.None; + } + + public long GetLong() + { + return 0L; + } + + public string GetString() + { + return ""; + } + + public void SetBoolean(bool val) + { + } + + public void SetDouble(double val) + { + } + + public void SetInt(int val) + { + } + + public void SetJsonType(JsonType type) + { + } + + public void SetLong(long val) + { + } + + public void SetString(string val) + { + } + + public string ToJson() + { + return ""; + } + + public void ToJson(JsonWriter writer) + { + } + + + bool IList.IsFixedSize + { + get { return true; } + } + + bool IList.IsReadOnly + { + get { return true; } + } + + object IList.this[int index] + { + get { return null; } + set { } + } + + int IList.Add(object value) + { + return 0; + } + + void IList.Clear() + { + } + + bool IList.Contains(object value) + { + return false; + } + + int IList.IndexOf(object value) + { + return -1; + } + + void IList.Insert(int i, object v) + { + } + + void IList.Remove(object value) + { + } + + void IList.RemoveAt(int index) + { + } + + + int ICollection.Count + { + get { return 0; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return null; } + } + + void ICollection.CopyTo(Array array, int index) + { + } + + + IEnumerator IEnumerable.GetEnumerator() + { + return null; + } + + + bool IDictionary.IsFixedSize + { + get { return true; } + } + + bool IDictionary.IsReadOnly + { + get { return true; } + } + + ICollection IDictionary.Keys + { + get { return null; } + } + + ICollection IDictionary.Values + { + get { return null; } + } + + object IDictionary.this[object key] + { + get { return null; } + set { } + } + + void IDictionary.Add(object k, object v) + { + } + + void IDictionary.Clear() + { + } + + bool IDictionary.Contains(object key) + { + return false; + } + + void IDictionary.Remove(object key) + { + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return null; + } + + + object IOrderedDictionary.this[int idx] + { + get { return null; } + set { } + } + + IDictionaryEnumerator IOrderedDictionary.GetEnumerator() + { + return null; + } + + void IOrderedDictionary.Insert(int i, object k, object v) + { + } + + void IOrderedDictionary.RemoveAt(int i) + { + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonReader.cs b/Source/Cake.Gitter/LitJson/JsonReader.cs new file mode 100644 index 0000000..8bf5425 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonReader.cs @@ -0,0 +1,533 @@ +#region Header +/* + * JsonReader.cs + * Stream-like access to JSON text. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + + + namespace LitJson + { + internal enum JsonToken + { + None, + + ObjectStart, + PropertyName, + ObjectEnd, + + ArrayStart, + ArrayEnd, + + Int, + Long, + Double, + + String, + + Boolean, + Null + } + + + internal class JsonReader + { + #region Fields + + private static readonly IDictionary> parse_table; + + private Stack automaton_stack; + private int current_input; + private int current_symbol; + private bool end_of_json; + private bool end_of_input; + private Lexer lexer; + private bool parser_in_string; + private bool parser_return; + private bool read_started; + private TextReader reader; + private bool reader_is_owned; + private bool skip_non_members; + private object token_value; + private JsonToken token; + + #endregion + + + #region Public Properties + + public bool AllowComments + { + get { return lexer.AllowComments; } + set { lexer.AllowComments = value; } + } + + public bool AllowSingleQuotedStrings + { + get { return lexer.AllowSingleQuotedStrings; } + set { lexer.AllowSingleQuotedStrings = value; } + } + + public bool SkipNonMembers + { + get { return skip_non_members; } + set { skip_non_members = value; } + } + + public bool EndOfInput + { + get { return end_of_input; } + } + + public bool EndOfJson + { + get { return end_of_json; } + } + + public JsonToken Token + { + get { return token; } + } + + public object Value + { + get { return token_value; } + } + + #endregion + + + #region Constructors + + static JsonReader() + { + parse_table = PopulateParseTable(); + } + + public JsonReader(string json_text) : + this(new StringReader(json_text), true) + { + } + + public JsonReader(TextReader reader) : + this(reader, false) + { + } + + private JsonReader(TextReader reader, bool owned) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + parser_in_string = false; + parser_return = false; + + read_started = false; + automaton_stack = new Stack(); + automaton_stack.Push((int) ParserToken.End); + automaton_stack.Push((int) ParserToken.Text); + + lexer = new Lexer(reader); + + end_of_input = false; + end_of_json = false; + + skip_non_members = true; + + this.reader = reader; + reader_is_owned = owned; + } + + #endregion + + + #region Static Methods + + private static IDictionary> PopulateParseTable() + { + // See section A.2. of the manual for details + IDictionary> parse_table = new Dictionary>(); + + TableAddRow(parse_table, ParserToken.Array); + TableAddCol(parse_table, ParserToken.Array, '[', + '[', + (int) ParserToken.ArrayPrime); + + TableAddRow(parse_table, ParserToken.ArrayPrime); + TableAddCol(parse_table, ParserToken.ArrayPrime, '"', + (int) ParserToken.Value, + + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, '[', + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, ']', + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, '{', + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, (int) ParserToken.True, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, (int) ParserToken.False, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol(parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + + TableAddRow(parse_table, ParserToken.Object); + TableAddCol(parse_table, ParserToken.Object, '{', + '{', + (int) ParserToken.ObjectPrime); + + TableAddRow(parse_table, ParserToken.ObjectPrime); + TableAddCol(parse_table, ParserToken.ObjectPrime, '"', + (int) ParserToken.Pair, + (int) ParserToken.PairRest, + '}'); + TableAddCol(parse_table, ParserToken.ObjectPrime, '}', + '}'); + + TableAddRow(parse_table, ParserToken.Pair); + TableAddCol(parse_table, ParserToken.Pair, '"', + (int) ParserToken.String, + ':', + (int) ParserToken.Value); + + TableAddRow(parse_table, ParserToken.PairRest); + TableAddCol(parse_table, ParserToken.PairRest, ',', + ',', + (int) ParserToken.Pair, + (int) ParserToken.PairRest); + TableAddCol(parse_table, ParserToken.PairRest, '}', + (int) ParserToken.Epsilon); + + TableAddRow(parse_table, ParserToken.String); + TableAddCol(parse_table, ParserToken.String, '"', + '"', + (int) ParserToken.CharSeq, + '"'); + + TableAddRow(parse_table, ParserToken.Text); + TableAddCol(parse_table, ParserToken.Text, '[', + (int) ParserToken.Array); + TableAddCol(parse_table, ParserToken.Text, '{', + (int) ParserToken.Object); + + TableAddRow(parse_table, ParserToken.Value); + TableAddCol(parse_table, ParserToken.Value, '"', + (int) ParserToken.String); + TableAddCol(parse_table, ParserToken.Value, '[', + (int) ParserToken.Array); + TableAddCol(parse_table, ParserToken.Value, '{', + (int) ParserToken.Object); + TableAddCol(parse_table, ParserToken.Value, (int) ParserToken.Number, + (int) ParserToken.Number); + TableAddCol(parse_table, ParserToken.Value, (int) ParserToken.True, + (int) ParserToken.True); + TableAddCol(parse_table, ParserToken.Value, (int) ParserToken.False, + (int) ParserToken.False); + TableAddCol(parse_table, ParserToken.Value, (int) ParserToken.Null, + (int) ParserToken.Null); + + TableAddRow(parse_table, ParserToken.ValueRest); + TableAddCol(parse_table, ParserToken.ValueRest, ',', + ',', + (int) ParserToken.Value, + (int) ParserToken.ValueRest); + TableAddCol(parse_table, ParserToken.ValueRest, ']', + (int) ParserToken.Epsilon); + + return parse_table; + } + + private static void TableAddCol(IDictionary> parse_table, ParserToken row, + int col, + params int[] symbols) + { + parse_table[(int) row].Add(col, symbols); + } + + private static void TableAddRow(IDictionary> parse_table, ParserToken rule) + { + parse_table.Add((int) rule, new Dictionary()); + } + + #endregion + + + #region Private Methods + + private void ProcessNumber(string number) + { + if (number.IndexOf('.') != -1 || + number.IndexOf('e') != -1 || + number.IndexOf('E') != -1) + { + + double n_double; + if (Double.TryParse(number, out n_double)) + { + token = JsonToken.Double; + token_value = n_double; + + return; + } + } + + int n_int32; + if (Int32.TryParse(number, out n_int32)) + { + token = JsonToken.Int; + token_value = n_int32; + + return; + } + + long n_int64; + if (Int64.TryParse(number, out n_int64)) + { + token = JsonToken.Long; + token_value = n_int64; + + return; + } + + ulong n_uint64; + if (UInt64.TryParse(number, out n_uint64)) + { + token = JsonToken.Long; + token_value = n_uint64; + + return; + } + + // Shouldn't happen, but just in case, return something + token = JsonToken.Int; + token_value = 0; + } + + private void ProcessSymbol() + { + if (current_symbol == '[') + { + token = JsonToken.ArrayStart; + parser_return = true; + + } + else if (current_symbol == ']') + { + token = JsonToken.ArrayEnd; + parser_return = true; + + } + else if (current_symbol == '{') + { + token = JsonToken.ObjectStart; + parser_return = true; + + } + else if (current_symbol == '}') + { + token = JsonToken.ObjectEnd; + parser_return = true; + + } + else if (current_symbol == '"') + { + if (parser_in_string) + { + parser_in_string = false; + + parser_return = true; + + } + else + { + if (token == JsonToken.None) + token = JsonToken.String; + + parser_in_string = true; + } + + } + else if (current_symbol == (int) ParserToken.CharSeq) + { + token_value = lexer.StringValue; + + } + else if (current_symbol == (int) ParserToken.False) + { + token = JsonToken.Boolean; + token_value = false; + parser_return = true; + + } + else if (current_symbol == (int) ParserToken.Null) + { + token = JsonToken.Null; + parser_return = true; + + } + else if (current_symbol == (int) ParserToken.Number) + { + ProcessNumber(lexer.StringValue); + + parser_return = true; + + } + else if (current_symbol == (int) ParserToken.Pair) + { + token = JsonToken.PropertyName; + + } + else if (current_symbol == (int) ParserToken.True) + { + token = JsonToken.Boolean; + token_value = true; + parser_return = true; + + } + } + + private bool ReadToken() + { + if (end_of_input) + return false; + + lexer.NextToken(); + + if (lexer.EndOfInput) + { + Close(); + + return false; + } + + current_input = lexer.Token; + + return true; + } + + #endregion + + + public void Close() + { + if (end_of_input) + return; + + end_of_input = true; + end_of_json = true; + + if (reader_is_owned) + reader.Dispose(); + + reader = null; + } + + public bool Read() + { + if (end_of_input) + return false; + + if (end_of_json) + { + end_of_json = false; + automaton_stack.Clear(); + automaton_stack.Push((int) ParserToken.End); + automaton_stack.Push((int) ParserToken.Text); + } + + parser_in_string = false; + parser_return = false; + + token = JsonToken.None; + token_value = null; + + if (!read_started) + { + read_started = true; + + if (!ReadToken()) + return false; + } + + + int[] entry_symbols; + + while (true) + { + if (parser_return) + { + if (automaton_stack.Peek() == (int) ParserToken.End) + end_of_json = true; + + return true; + } + + current_symbol = automaton_stack.Pop(); + + ProcessSymbol(); + + if (current_symbol == current_input) + { + if (!ReadToken()) + { + if (automaton_stack.Peek() != (int) ParserToken.End) + throw new JsonException( + "Input doesn't evaluate to proper JSON text"); + + if (parser_return) + return true; + + return false; + } + + continue; + } + + try + { + + entry_symbols = + parse_table[current_symbol][current_input]; + + } + catch (KeyNotFoundException e) + { + throw new JsonException((ParserToken) current_input, e); + } + + if (entry_symbols[0] == (int) ParserToken.Epsilon) + continue; + + for (int i = entry_symbols.Length - 1; i >= 0; i--) + automaton_stack.Push(entry_symbols[i]); + } + } + + } + } +} diff --git a/Source/Cake.Gitter/LitJson/JsonWriter.cs b/Source/Cake.Gitter/LitJson/JsonWriter.cs new file mode 100644 index 0000000..12afe57 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/JsonWriter.cs @@ -0,0 +1,498 @@ +#region Header +/* + * JsonWriter.cs + * Stream-like facility to output JSON text. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Text; + + + namespace LitJson + { + internal enum Condition + { + InArray, + InObject, + NotAProperty, + Property, + Value + } + + internal class WriterContext + { + public int Count; + public bool InArray; + public bool InObject; + public bool ExpectingValue; + public int Padding; + } + + internal class JsonWriter + { + #region Fields + + private static readonly NumberFormatInfo number_format; + + private WriterContext context; + private Stack ctx_stack; + private bool has_reached_end; + private char[] hex_seq; + private int indentation; + private int indent_value; + private StringBuilder inst_string_builder; + private bool pretty_print; + private bool validate; + private bool lower_case_properties; + private TextWriter writer; + + #endregion + + + #region Properties + + public int IndentValue + { + get { return indent_value; } + set + { + indentation = (indentation/indent_value)*value; + indent_value = value; + } + } + + public bool PrettyPrint + { + get { return pretty_print; } + set { pretty_print = value; } + } + + public TextWriter TextWriter + { + get { return writer; } + } + + public bool Validate + { + get { return validate; } + set { validate = value; } + } + + public bool LowerCaseProperties + { + get { return lower_case_properties; } + set { lower_case_properties = value; } + } + + #endregion + + + #region Constructors + + static JsonWriter() + { + number_format = NumberFormatInfo.InvariantInfo; + } + + public JsonWriter() + { + inst_string_builder = new StringBuilder(); + writer = new StringWriter(inst_string_builder); + + Init(); + } + + public JsonWriter(StringBuilder sb) : + this(new StringWriter(sb)) + { + } + + public JsonWriter(TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + this.writer = writer; + + Init(); + } + + #endregion + + + #region Private Methods + + private void DoValidation(Condition cond) + { + if (!context.ExpectingValue) + context.Count++; + + if (!validate) + return; + + if (has_reached_end) + throw new JsonException( + "A complete JSON symbol has already been written"); + + switch (cond) + { + case Condition.InArray: + if (!context.InArray) + throw new JsonException( + "Can't close an array here"); + break; + + case Condition.InObject: + if (!context.InObject || context.ExpectingValue) + throw new JsonException( + "Can't close an object here"); + break; + + case Condition.NotAProperty: + if (context.InObject && !context.ExpectingValue) + throw new JsonException( + "Expected a property"); + break; + + case Condition.Property: + if (!context.InObject || context.ExpectingValue) + throw new JsonException( + "Can't add a property here"); + break; + + case Condition.Value: + if (!context.InArray && + (!context.InObject || !context.ExpectingValue)) + throw new JsonException( + "Can't add a value here"); + + break; + } + } + + private void Init() + { + has_reached_end = false; + hex_seq = new char[4]; + indentation = 0; + indent_value = 4; + pretty_print = true; + validate = true; + lower_case_properties = false; + + ctx_stack = new Stack(); + context = new WriterContext(); + ctx_stack.Push(context); + } + + private static void IntToHex(int n, char[] hex) + { + int num; + + for (int i = 0; i < 4; i++) + { + num = n%16; + + if (num < 10) + hex[3 - i] = (char) ('0' + num); + else + hex[3 - i] = (char) ('A' + (num - 10)); + + n >>= 4; + } + } + + private void Indent() + { + if (pretty_print) + indentation += indent_value; + } + + + private void Put(string str) + { + if (pretty_print && !context.ExpectingValue) + for (int i = 0; i < indentation; i++) + writer.Write(' '); + + writer.Write(str); + } + + private void PutNewline() + { + PutNewline(true); + } + + private void PutNewline(bool add_comma) + { + if (add_comma && !context.ExpectingValue && + context.Count > 1) + writer.Write(','); + + if (pretty_print && !context.ExpectingValue) + writer.Write('\n'); + } + + private void PutString(string str) + { + Put(String.Empty); + + writer.Write('"'); + + int n = str.Length; + for (int i = 0; i < n; i++) + { + switch (str[i]) + { + case '\n': + writer.Write("\\n"); + continue; + + case '\r': + writer.Write("\\r"); + continue; + + case '\t': + writer.Write("\\t"); + continue; + + case '"': + case '\\': + writer.Write('\\'); + writer.Write(str[i]); + continue; + + case '\f': + writer.Write("\\f"); + continue; + + case '\b': + writer.Write("\\b"); + continue; + } + + if ((int) str[i] >= 32 && (int) str[i] <= 126) + { + writer.Write(str[i]); + continue; + } + + // Default, turn into a \uXXXX sequence + IntToHex((int) str[i], hex_seq); + writer.Write("\\u"); + writer.Write(hex_seq); + } + + writer.Write('"'); + } + + private void Unindent() + { + if (pretty_print) + indentation -= indent_value; + } + + #endregion + + + public override string ToString() + { + if (inst_string_builder == null) + return String.Empty; + + return inst_string_builder.ToString(); + } + + public void Reset() + { + has_reached_end = false; + + ctx_stack.Clear(); + context = new WriterContext(); + ctx_stack.Push(context); + + if (inst_string_builder != null) + inst_string_builder.Remove(0, inst_string_builder.Length); + } + + public void Write(bool boolean) + { + DoValidation(Condition.Value); + PutNewline(); + + Put(boolean ? "true" : "false"); + + context.ExpectingValue = false; + } + + public void Write(decimal number) + { + DoValidation(Condition.Value); + PutNewline(); + + Put(Convert.ToString(number, number_format)); + + context.ExpectingValue = false; + } + + public void Write(double number) + { + DoValidation(Condition.Value); + PutNewline(); + + string str = Convert.ToString(number, number_format); + Put(str); + + if (str.IndexOf('.') == -1 && + str.IndexOf('E') == -1) + writer.Write(".0"); + + context.ExpectingValue = false; + } + + public void Write(int number) + { + DoValidation(Condition.Value); + PutNewline(); + + Put(Convert.ToString(number, number_format)); + + context.ExpectingValue = false; + } + + public void Write(long number) + { + DoValidation(Condition.Value); + PutNewline(); + + Put(Convert.ToString(number, number_format)); + + context.ExpectingValue = false; + } + + public void Write(string str) + { + DoValidation(Condition.Value); + PutNewline(); + + if (str == null) + Put("null"); + else + PutString(str); + + context.ExpectingValue = false; + } + + public void Write(ulong number) + { + DoValidation(Condition.Value); + PutNewline(); + + Put(Convert.ToString(number, number_format)); + + context.ExpectingValue = false; + } + + public void WriteArrayEnd() + { + DoValidation(Condition.InArray); + PutNewline(false); + + ctx_stack.Pop(); + if (ctx_stack.Count == 1) + has_reached_end = true; + else + { + context = ctx_stack.Peek(); + context.ExpectingValue = false; + } + + Unindent(); + Put("]"); + } + + public void WriteArrayStart() + { + DoValidation(Condition.NotAProperty); + PutNewline(); + + Put("["); + + context = new WriterContext(); + context.InArray = true; + ctx_stack.Push(context); + + Indent(); + } + + public void WriteObjectEnd() + { + DoValidation(Condition.InObject); + PutNewline(false); + + ctx_stack.Pop(); + if (ctx_stack.Count == 1) + has_reached_end = true; + else + { + context = ctx_stack.Peek(); + context.ExpectingValue = false; + } + + Unindent(); + Put("}"); + } + + public void WriteObjectStart() + { + DoValidation(Condition.NotAProperty); + PutNewline(); + + Put("{"); + + context = new WriterContext(); + context.InObject = true; + ctx_stack.Push(context); + + Indent(); + } + + public void WritePropertyName(string property_name) + { + DoValidation(Condition.Property); + PutNewline(); + string propertyName = (property_name == null || !lower_case_properties) + ? property_name + : property_name.ToLowerInvariant(); + + PutString(propertyName); + + if (pretty_print) + { + if (propertyName.Length > context.Padding) + context.Padding = propertyName.Length; + + for (int i = context.Padding - propertyName.Length; + i >= 0; + i--) + writer.Write(' '); + + writer.Write(": "); + } + else + writer.Write(':'); + + context.ExpectingValue = true; + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/Lexer.cs b/Source/Cake.Gitter/LitJson/Lexer.cs new file mode 100644 index 0000000..1f26e59 --- /dev/null +++ b/Source/Cake.Gitter/LitJson/Lexer.cs @@ -0,0 +1,983 @@ +#region Header +/* + * Lexer.cs + * JSON lexer implementation based on a finite state machine. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + + + namespace LitJson + { + internal class FsmContext + { + public bool Return; + public int NextState; + public Lexer L; + public int StateStack; + } + + + internal class Lexer + { + #region Fields + + private delegate bool StateHandler(FsmContext ctx); + + private static readonly int[] fsm_return_table; + private static readonly StateHandler[] fsm_handler_table; + + private bool allow_comments; + private bool allow_single_quoted_strings; + private bool end_of_input; + private FsmContext fsm_context; + private int input_buffer; + private int input_char; + private TextReader reader; + private int state; + private StringBuilder string_buffer; + private string string_value; + private int token; + private int unichar; + + #endregion + + + #region Properties + + public bool AllowComments + { + get { return allow_comments; } + set { allow_comments = value; } + } + + public bool AllowSingleQuotedStrings + { + get { return allow_single_quoted_strings; } + set { allow_single_quoted_strings = value; } + } + + public bool EndOfInput + { + get { return end_of_input; } + } + + public int Token + { + get { return token; } + } + + public string StringValue + { + get { return string_value; } + } + + #endregion + + + #region Constructors + + static Lexer() + { + PopulateFsmTables(out fsm_handler_table, out fsm_return_table); + } + + public Lexer(TextReader reader) + { + allow_comments = true; + allow_single_quoted_strings = true; + + input_buffer = 0; + string_buffer = new StringBuilder(128); + state = 1; + end_of_input = false; + this.reader = reader; + + fsm_context = new FsmContext(); + fsm_context.L = this; + } + + #endregion + + + #region Static Methods + + private static int HexValue(int digit) + { + switch (digit) + { + case 'a': + case 'A': + return 10; + + case 'b': + case 'B': + return 11; + + case 'c': + case 'C': + return 12; + + case 'd': + case 'D': + return 13; + + case 'e': + case 'E': + return 14; + + case 'f': + case 'F': + return 15; + + default: + return digit - '0'; + } + } + + private static void PopulateFsmTables(out StateHandler[] fsm_handler_table, out int[] fsm_return_table) + { + // See section A.1. of the manual for details of the finite + // state machine. + fsm_handler_table = new StateHandler[28] + { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9, + State10, + State11, + State12, + State13, + State14, + State15, + State16, + State17, + State18, + State19, + State20, + State21, + State22, + State23, + State24, + State25, + State26, + State27, + State28 + }; + + fsm_return_table = new int[28] + { + (int) ParserToken.Char, + 0, + (int) ParserToken.Number, + (int) ParserToken.Number, + 0, + (int) ParserToken.Number, + 0, + (int) ParserToken.Number, + 0, + 0, + (int) ParserToken.True, + 0, + 0, + 0, + (int) ParserToken.False, + 0, + 0, + (int) ParserToken.Null, + (int) ParserToken.CharSeq, + (int) ParserToken.Char, + 0, + 0, + (int) ParserToken.CharSeq, + (int) ParserToken.Char, + 0, + 0, + 0, + 0 + }; + } + + private static char ProcessEscChar(int esc_char) + { + switch (esc_char) + { + case '"': + case '\'': + case '\\': + case '/': + return Convert.ToChar(esc_char); + + case 'n': + return '\n'; + + case 't': + return '\t'; + + case 'r': + return '\r'; + + case 'b': + return '\b'; + + case 'f': + return '\f'; + + default: + // Unreachable + return '?'; + } + } + + private static bool State1(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + continue; + + if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch (ctx.L.input_char) + { + case '"': + ctx.NextState = 19; + ctx.Return = true; + return true; + + case ',': + case ':': + case '[': + case ']': + case '{': + case '}': + ctx.NextState = 1; + ctx.Return = true; + return true; + + case '-': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 2; + return true; + + case '0': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 4; + return true; + + case 'f': + ctx.NextState = 12; + return true; + + case 'n': + ctx.NextState = 16; + return true; + + case 't': + ctx.NextState = 9; + return true; + + case '\'': + if (!ctx.L.allow_single_quoted_strings) + return false; + + ctx.L.input_char = '"'; + ctx.NextState = 23; + ctx.Return = true; + return true; + + case '/': + if (!ctx.L.allow_comments) + return false; + + ctx.NextState = 25; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State2(FsmContext ctx) + { + ctx.L.GetChar(); + + if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch (ctx.L.input_char) + { + case '0': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 4; + return true; + + default: + return false; + } + } + + private static bool State3(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) + { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + return true; + } + + private static bool State4(FsmContext ctx) + { + ctx.L.GetChar(); + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) + { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + private static bool State5(FsmContext ctx) + { + ctx.L.GetChar(); + + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 6; + return true; + } + + return false; + } + + private static bool State6(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) + { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State7(FsmContext ctx) + { + ctx.L.GetChar(); + + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 8; + return true; + } + + switch (ctx.L.input_char) + { + case '+': + case '-': + ctx.L.string_buffer.Append((char) ctx.L.input_char); + ctx.NextState = 8; + return true; + + default: + return false; + } + } + + private static bool State8(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') + { + ctx.L.string_buffer.Append((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) + { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State9(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'r': + ctx.NextState = 10; + return true; + + default: + return false; + } + } + + private static bool State10(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'u': + ctx.NextState = 11; + return true; + + default: + return false; + } + } + + private static bool State11(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State12(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'a': + ctx.NextState = 13; + return true; + + default: + return false; + } + } + + private static bool State13(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'l': + ctx.NextState = 14; + return true; + + default: + return false; + } + } + + private static bool State14(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 's': + ctx.NextState = 15; + return true; + + default: + return false; + } + } + + private static bool State15(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State16(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'u': + ctx.NextState = 17; + return true; + + default: + return false; + } + } + + private static bool State17(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'l': + ctx.NextState = 18; + return true; + + default: + return false; + } + } + + private static bool State18(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'l': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State19(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + switch (ctx.L.input_char) + { + case '"': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 20; + return true; + + case '\\': + ctx.StateStack = 19; + ctx.NextState = 21; + return true; + + default: + ctx.L.string_buffer.Append((char) ctx.L.input_char); + continue; + } + } + + return true; + } + + private static bool State20(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case '"': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State21(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case 'u': + ctx.NextState = 22; + return true; + + case '"': + case '\'': + case '/': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + ctx.L.string_buffer.Append( + ProcessEscChar(ctx.L.input_char)); + ctx.NextState = ctx.StateStack; + return true; + + default: + return false; + } + } + + private static bool State22(FsmContext ctx) + { + int counter = 0; + int mult = 4096; + + ctx.L.unichar = 0; + + while (ctx.L.GetChar()) + { + + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || + ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || + ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') + { + + ctx.L.unichar += HexValue(ctx.L.input_char)*mult; + + counter++; + mult /= 16; + + if (counter == 4) + { + ctx.L.string_buffer.Append( + Convert.ToChar(ctx.L.unichar)); + ctx.NextState = ctx.StateStack; + return true; + } + + continue; + } + + return false; + } + + return true; + } + + private static bool State23(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + switch (ctx.L.input_char) + { + case '\'': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 24; + return true; + + case '\\': + ctx.StateStack = 23; + ctx.NextState = 21; + return true; + + default: + ctx.L.string_buffer.Append((char) ctx.L.input_char); + continue; + } + } + + return true; + } + + private static bool State24(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case '\'': + ctx.L.input_char = '"'; + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State25(FsmContext ctx) + { + ctx.L.GetChar(); + + switch (ctx.L.input_char) + { + case '*': + ctx.NextState = 27; + return true; + + case '/': + ctx.NextState = 26; + return true; + + default: + return false; + } + } + + private static bool State26(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char == '\n') + { + ctx.NextState = 1; + return true; + } + } + + return true; + } + + private static bool State27(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char == '*') + { + ctx.NextState = 28; + return true; + } + } + + return true; + } + + private static bool State28(FsmContext ctx) + { + while (ctx.L.GetChar()) + { + if (ctx.L.input_char == '*') + continue; + + if (ctx.L.input_char == '/') + { + ctx.NextState = 1; + return true; + } + + ctx.NextState = 27; + return true; + } + + return true; + } + + #endregion + + + private bool GetChar() + { + if ((input_char = NextChar()) != -1) + return true; + + end_of_input = true; + return false; + } + + private int NextChar() + { + if (input_buffer != 0) + { + int tmp = input_buffer; + input_buffer = 0; + + return tmp; + } + + return reader.Read(); + } + + public bool NextToken() + { + StateHandler handler; + fsm_context.Return = false; + + while (true) + { + handler = fsm_handler_table[state - 1]; + + if (!handler(fsm_context)) + throw new JsonException(input_char); + + if (end_of_input) + return false; + + if (fsm_context.Return) + { + string_value = string_buffer.ToString(); + string_buffer.Remove(0, string_buffer.Length); + token = fsm_return_table[state - 1]; + + if (token == (int) ParserToken.Char) + token = input_char; + + state = fsm_context.NextState; + + return true; + } + + state = fsm_context.NextState; + } + } + + private void UngetChar() + { + input_buffer = input_char; + } + } + } +} diff --git a/Source/Cake.Gitter/LitJson/ParserToken.cs b/Source/Cake.Gitter/LitJson/ParserToken.cs new file mode 100644 index 0000000..0fc69dd --- /dev/null +++ b/Source/Cake.Gitter/LitJson/ParserToken.cs @@ -0,0 +1,46 @@ +#region Header +/* + * ParserToken.cs + * Internal representation of the tokens used by the lexer and the parser. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + */ +#endregion + +namespace Cake.Gitter +{ + namespace LitJson + { + internal enum ParserToken + { + // Lexer tokens (see section A.1.1. of the manual) + None = System.Char.MaxValue + 1, + Number, + True, + False, + Null, + CharSeq, + // Single char + Char, + + // Parser Rules (see section A.2.1 of the manual) + Text, + Object, + ObjectPrime, + Pair, + PairRest, + Array, + ArrayPrime, + Value, + ValueRest, + String, + + // End of input + End, + + // The empty rule + Epsilon + } + } +} From dc85d69777fa1e0865ed8bc6d97c9ea5239326cf Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 24 Oct 2018 22:34:24 +0100 Subject: [PATCH 2/6] (maint) Add .DS_Store --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d32f3bc..76be0b2 100644 --- a/.gitignore +++ b/.gitignore @@ -197,4 +197,7 @@ FakesAssemblies/ # Build Related tools/ -BuildArtifacts/ \ No newline at end of file +BuildArtifacts/ + +# Mac +.DS_Store From def059964e6e649485bde1e62fc1882bb01359bd Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 24 Oct 2018 22:34:40 +0100 Subject: [PATCH 3/6] (build) Update to latest Cake --- Tools/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/packages.config b/Tools/packages.config index 31f2b2a..84d0eb4 100644 --- a/Tools/packages.config +++ b/Tools/packages.config @@ -1,4 +1,4 @@ - + From 95789a38a06c92efbcf0df22f54b942f601a80ec Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 24 Oct 2018 22:35:58 +0100 Subject: [PATCH 4/6] (GH-34) Switch away from GitterSharp.dll --- Source/Cake.Gitter/Chat/GitterChatApi.cs | 70 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/Source/Cake.Gitter/Chat/GitterChatApi.cs b/Source/Cake.Gitter/Chat/GitterChatApi.cs index e34261f..3e626b4 100644 --- a/Source/Cake.Gitter/Chat/GitterChatApi.cs +++ b/Source/Cake.Gitter/Chat/GitterChatApi.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Cake.Common.Diagnostics; using Cake.Core; -using GitterSharp.Model.Webhook; -using GitterSharp.Services; +using Cake.Gitter.LitJson; namespace Cake.Gitter.Chat { @@ -24,11 +27,11 @@ internal static GitterChatMessageResult PostMessage(this ICakeContext context, s GitterChatMessageResult result; if (!string.IsNullOrWhiteSpace(messageSettings.IncomingWebHookUrl)) { - result = PostToIncomingWebHook(context, message, messageSettings); + result = PostToIncomingWebHook(context, message, messageSettings).Result; } else { - result = context.PostToChatApi(message, messageSettings); + result = context.PostToChatApi(message, messageSettings).Result; } if (!result.Ok && messageSettings.ThrowOnFail == true) @@ -39,35 +42,76 @@ internal static GitterChatMessageResult PostMessage(this ICakeContext context, s return result; } - private static GitterChatMessageResult PostToIncomingWebHook(ICakeContext context, string message, GitterChatMessageSettings messageSettings) + private static async Task PostToIncomingWebHook(ICakeContext context, string message, GitterChatMessageSettings messageSettings) { context.Verbose("Posting to incoming webhook {0}...", string.Concat(messageSettings.IncomingWebHookUrl.TrimEnd('/').Reverse().SkipWhile(c => c != '/').Reverse())); - var gitterWebHookService = new WebhookService(); - var result = gitterWebHookService.PostAsync(messageSettings.IncomingWebHookUrl, message, messageSettings.MessageLevel == GitterMessageLevel.Error ? MessageLevel.Error : MessageLevel.Info); + var httpClient = new HttpClient(); - var parsedResult = new GitterChatMessageResult(result.Result, DateTime.UtcNow.ToString("u"), string.Empty); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json"); + + var content = new FormUrlEncodedContent(new Dictionary + { + {"message", message}, + {"level", messageSettings.MessageLevel == GitterMessageLevel.Error ? "error" : "info"} + }); + + var httpResponse = await httpClient.PostAsync(new Uri(messageSettings.IncomingWebHookUrl), content); + var response = await httpResponse.Content.ReadAsStringAsync(); + var result = JsonMapper.ToObject(response); + + var parsedResult = new GitterChatMessageResult( + httpResponse.IsSuccessStatusCode, + DateTime.UtcNow.ToString("u"), + string.Empty); context.Debug("Result parsed: {0}", parsedResult); return parsedResult; } - private static GitterChatMessageResult PostToChatApi(this ICakeContext context, string message, GitterChatMessageSettings messageSettings) + private static async Task PostToChatApi(this ICakeContext context, string message, GitterChatMessageSettings messageSettings) { if (string.IsNullOrWhiteSpace(messageSettings.Token)) { throw new NullReferenceException("No authorization token provided."); } - var gitterApiService = new GitterApiService(messageSettings.Token); - var messageResponse = gitterApiService.SendMessageAsync(messageSettings.RoomId, message); + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var parsedResult = new GitterChatMessageResult(!string.IsNullOrWhiteSpace(messageResponse.Result.Id), messageResponse.Result.SentDate.ToString("u"), string.Empty); + if (!string.IsNullOrWhiteSpace(messageSettings.Token)) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", messageSettings.Token); + } + + string url = $"https://api.gitter.im/v1/rooms/{messageSettings.RoomId}/chatMessages"; + + var content = new FormUrlEncodedContent(new Dictionary + { + {"text", message} + }); + + var httpResponse = await httpClient.PostAsync(url, content); + var response = await httpResponse.Content.ReadAsStringAsync(); + + var result = JsonMapper.ToObject(response); + var parsedResult = new GitterChatMessageResult( + httpResponse.IsSuccessStatusCode, + result.GetString("sent"), + string.Empty); context.Debug("Result parsed: {0}", parsedResult); return parsedResult; } + + private static string GetString(this JsonData data, string key) + { + return (data != null && data.Keys.Contains(key)) + ? (string)data[key] + : null; + } } -} \ No newline at end of file +} From 12001471e5f684323bcd793dfe5455717b3e488d Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 24 Oct 2018 22:46:51 +0100 Subject: [PATCH 5/6] (build) Exclude LitJson from analysis --- setup.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cake b/setup.cake index 055c0e0..90cddb8 100644 --- a/setup.cake +++ b/setup.cake @@ -15,7 +15,7 @@ BuildParameters.PrintParameters(Context); ToolSettings.SetToolSettings(context: Context, dupFinderExcludePattern: new string[] { - BuildParameters.RootDirectoryPath + "/Source/Cake.Gitter.Tests/*.cs", BuildParameters.RootDirectoryPath + "/Source/Cake.Gitter/**/*.AssemblyInfo.cs" }, + BuildParameters.RootDirectoryPath + "/Source/Cake.Gitter.Tests/*.cs", BuildParameters.RootDirectoryPath + "/Source/Cake.Gitter/**/*.AssemblyInfo.cs", BuildParameters.RootDirectoryPath + "/Source/Cake.Gitter/LitJson/*.cs" }, testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* ", testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*", testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"); From dbefb1a675f93cb1a4836a250c71e14f9d715219 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Fri, 26 Oct 2018 20:16:25 +0100 Subject: [PATCH 6/6] (GH-34) Removed gitter-api-pcl and stylecop packages --- Source/Cake.Gitter/Cake.Gitter.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Cake.Gitter/Cake.Gitter.csproj b/Source/Cake.Gitter/Cake.Gitter.csproj index a7d6908..001fe91 100644 --- a/Source/Cake.Gitter/Cake.Gitter.csproj +++ b/Source/Cake.Gitter/Cake.Gitter.csproj @@ -12,7 +12,5 @@ - -