From e6181c24124d97f2fbc932b8a68311e625463156 Mon Sep 17 00:00:00 2001 From: uzvkl Date: Tue, 11 Jun 2019 23:05:52 +0200 Subject: Move dsa related stuff to subfolder --- dsa/FireBase/Streaming/FirebaseCache.cs | 181 ++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 dsa/FireBase/Streaming/FirebaseCache.cs (limited to 'dsa/FireBase/Streaming/FirebaseCache.cs') diff --git a/dsa/FireBase/Streaming/FirebaseCache.cs b/dsa/FireBase/Streaming/FirebaseCache.cs new file mode 100644 index 0000000..66241e0 --- /dev/null +++ b/dsa/FireBase/Streaming/FirebaseCache.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Firebase.Database.Http; +using Newtonsoft.Json; + +namespace Firebase.Database.Streaming +{ + /// + /// The firebase cache. + /// + /// Type of top-level entities in the cache. + public class FirebaseCache : IEnumerable> + { + private readonly IDictionary dictionary; + private readonly bool isDictionaryType; + + private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings + { + ObjectCreationHandling = ObjectCreationHandling.Replace + }; + + /// + /// Initializes a new instance of the class. + /// + public FirebaseCache() + : this(new Dictionary()) + { + } + + /// + /// Initializes a new instance of the class and populates it with existing data. + /// + /// The existing items. + public FirebaseCache(IDictionary existingItems) + { + dictionary = existingItems; + isDictionaryType = typeof(IDictionary).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()); + } + + /// + /// The push data. + /// + /// The path of incoming data, separated by slash. + /// The data in json format as returned by firebase. + /// Collection of top-level entities which were affected by the push. + public IEnumerable> PushData(string path, string data, bool removeEmptyEntries = true) + { + object obj = this.dictionary; + Action primitiveObjSetter = null; + Action objDeleter = null; + + var pathElements = path.Split(new[] {"/"}, + removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); + + // first find where we should insert the data to + foreach (var element in pathElements) + if (obj is IDictionary) + { + // if it's a dictionary, then it's just a matter of inserting into it / accessing existing object by key + var dictionary = obj as IDictionary; + var valueType = obj.GetType().GenericTypeArguments[1]; + + primitiveObjSetter = d => dictionary[element] = d; + objDeleter = () => dictionary.Remove(element); + + if (dictionary.Contains(element)) + { + obj = dictionary[element]; + } + else + { + dictionary[element] = CreateInstance(valueType); + obj = dictionary[element]; + } + } + else + { + // if it's not a dictionary, try to find the property of current object with the matching name + var objParent = obj; + var property = objParent + .GetType() + .GetRuntimeProperties() + .First(p => p.Name.Equals(element, StringComparison.OrdinalIgnoreCase) || + element == p.GetCustomAttribute()?.PropertyName); + + objDeleter = () => property.SetValue(objParent, null); + primitiveObjSetter = d => property.SetValue(objParent, d); + obj = property.GetValue(obj); + if (obj == null) + { + obj = CreateInstance(property.PropertyType); + property.SetValue(objParent, obj); + } + } + + // if data is null (=empty string) delete it + if (string.IsNullOrWhiteSpace(data) || data == "null") + { + var key = pathElements[0]; + var target = dictionary[key]; + + objDeleter(); + + yield return new FirebaseObject(key, target); + yield break; + } + + // now insert the data + if (obj is IDictionary && !isDictionaryType) + { + // insert data into dictionary and return it as a collection of FirebaseObject + var dictionary = obj as IDictionary; + var valueType = obj.GetType().GenericTypeArguments[1]; + var objectCollection = data.GetObjectCollection(valueType); + + foreach (var item in objectCollection) + { + dictionary[item.Key] = item.Object; + + // top level dictionary changed + if (!pathElements.Any()) yield return new FirebaseObject(item.Key, (T) item.Object); + } + + // nested dictionary changed + if (pathElements.Any()) + { + this.dictionary[pathElements[0]] = this.dictionary[pathElements[0]]; + yield return new FirebaseObject(pathElements[0], this.dictionary[pathElements[0]]); + } + } + else + { + // set the data on a property of the given object + var valueType = obj.GetType(); + + // firebase sends strings without double quotes + var targetObject = valueType == typeof(string) + ? data + : JsonConvert.DeserializeObject(data, valueType); + + if ((valueType.GetTypeInfo().IsPrimitive || valueType == typeof(string)) && primitiveObjSetter != null) + // handle primitive (value) types separately + primitiveObjSetter(targetObject); + else + JsonConvert.PopulateObject(data, obj, serializerSettings); + + dictionary[pathElements[0]] = dictionary[pathElements[0]]; + yield return new FirebaseObject(pathElements[0], dictionary[pathElements[0]]); + } + } + + public bool Contains(string key) + { + return dictionary.Keys.Contains(key); + } + + private object CreateInstance(Type type) + { + if (type == typeof(string)) + return string.Empty; + return Activator.CreateInstance(type); + } + + #region IEnumerable + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + return dictionary.Select(p => new FirebaseObject(p.Key, p.Value)).GetEnumerator(); + } + + #endregion + } +} \ No newline at end of file -- cgit v1.2.3-54-g00ecf