using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace ThinkingAnalytics.Utils { public class TD_PropertiesChecker { private static readonly Regex keyPattern = new Regex(@"^[a-zA-Z][a-zA-Z\d_#]{0,49}$"); private static readonly List propertyNameWhitelist = new List() { "#scene_name", "#scene_path", "#app_crashed_reason" }; public static bool IsNumeric(object obj) { return obj is sbyte || obj is byte || obj is short || obj is ushort || obj is int || obj is uint || obj is long || obj is ulong || obj is double || obj is decimal || obj is float; } public static bool IsString(object obj) { if (obj == null) return false; return obj is string; } public static bool IsDictionary(object obj) { if (obj == null) return false; return (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(Dictionary<,>)); } public static bool IsList(object obj) { if (obj == null) return false; return (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(List<>)) || obj is Array; } public static bool CheckProperties(Dictionary properties) { if (properties == null) { return true; } bool ret = true; foreach(KeyValuePair kv in properties) { if (!CheckString(kv.Key)) { ret = false; } if (!(kv.Value is string || kv.Value is DateTime || kv.Value is bool || IsNumeric(kv.Value) || IsList(kv.Value) || IsDictionary(kv.Value))) { if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - property values must be one of: string, numberic, Boolean, DateTime, Array, Row"); ret = false; } if (IsString(kv.Value) && !CheckProperties(kv.Value as string)) { ret = false; } if (IsNumeric(kv.Value)) { double number = Convert.ToDouble(kv.Value); if (!CheckProperties(number)) { ret = false; } } if (IsList(kv.Value) && !CheckProperties(kv.Value as List)) { ret = false; } if (IsDictionary(kv.Value) && !CheckProperties(kv.Value as Dictionary)) { ret = false; } } return ret; } public static bool CheckProperties(List properties) { if (properties == null) { return true; } bool ret = true; foreach (object value in properties) { if (!(value is string || value is DateTime || value is bool || IsNumeric(value) || IsDictionary(value))) { if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - property values in list must be one of: string, numberic, Boolean, DateTime, Row"); ret = false; } if (IsString(value) && !CheckProperties(value as string)) { ret = false; } if (IsNumeric(value)) { double number = Convert.ToDouble(value); if (!CheckProperties(number)) { ret = false; } } if (IsDictionary(value) && !CheckProperties(value as Dictionary)) { ret = false; } } return ret; } public static bool CheckProperties(List properties) { if (properties == null) { return true; } bool ret = true; foreach(string value in properties) { if (!CheckProperties(value)) { ret = false; } } return ret; } public static bool CheckProperties(string properties) { if (properties is string && System.Text.Encoding.UTF8.GetBytes(Convert.ToString(properties)).Length > 2048) { if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - the string is too long: " + (string)(object)properties); return false; } return true; } public static bool CheckProperties(double properties) { if (properties > 9999999999999.999 || properties < -9999999999999.999) { if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - number value is invalid: " + properties + ", the data range is -9E15 to 9E15, with a maximum of 3 decimal places"); return false; } return true; } public static bool CheckString(string eventName) { if (string.IsNullOrEmpty(eventName)) { if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - the string is null"); return false; } if (keyPattern.IsMatch(eventName)) { return true; } else { if (propertyNameWhitelist.Contains(eventName)) { return true; } if(TD_Log.GetEnable()) TD_Log.w("TA.PropertiesChecker - the string is invalid for TA: " + eventName + ", event name and properties name rules: must start with a letter, and can only contain: numbers, letters (ignoring case) and underscore(_), and the maximum length is 50."); return false; } } public static void MergeProperties(Dictionary source, Dictionary dest) { if (null == source) return; foreach (KeyValuePair kv in source) { if (dest.ContainsKey(kv.Key)) { dest[kv.Key] = kv.Value; } else { dest.Add(kv.Key, kv.Value); } } } } }