using System; using System.Collections.Generic; using System.Reflection; namespace DCFApixels.DragonECS { public static class EcsDebugUtility { #region GetGenericTypeName public static string GetGenericTypeFullName(int maxDepth = 2) => GetGenericTypeFullName(typeof(T), maxDepth); public static string GetGenericTypeFullName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, true); public static string GetGenericTypeName(int maxDepth = 2) => GetGenericTypeName(typeof(T), maxDepth); public static string GetGenericTypeName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, false); private static string GetGenericTypeNameInternal(Type type, int maxDepth, bool isFull) { #if (DEBUG && !DISABLE_DEBUG) string result = isFull ? type.FullName : type.Name; if (!type.IsGenericType || maxDepth == 0) return result; int iBacktick = result.IndexOf('`'); if (iBacktick > 0) result = result.Remove(iBacktick); result += "<"; Type[] typeParameters = type.GetGenericArguments(); for (int i = 0; i < typeParameters.Length; ++i) { string typeParamName = GetGenericTypeNameInternal(typeParameters[i], maxDepth - 1, false);//чтобы строка не была слишком длинной, используются сокращенные имена для типов аргументов result += (i == 0 ? typeParamName : "," + typeParamName); } result += ">"; return result; #else //optimization for release build return type.Name; #endif } #endregion #region AutoToString /// slow but automatic conversion of ValueType to string in the format "name(field1, field2... fieldn)" public static string AutoToString(this T self, bool isWriteName = true) where T : struct { return AutoToString(self, typeof(T), isWriteName); } private static string AutoToString(object target, Type type, bool isWriteName) { var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string[] values = new string[fields.Length]; for (int i = 0; i < fields.Length; i++) values[i] = (fields[i].GetValue(target) ?? "NULL").ToString(); if (isWriteName) return $"{type.Name}({string.Join(", ", values)})"; else return $"({string.Join(", ", values)})"; } #endregion #region GetName public static string GetName() => GetName(typeof(T)); public static string GetName(Type type) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type); public static bool TryGetCustomName(out string name) => TryGetCustomName(typeof(T), out name); public static bool TryGetCustomName(Type type, out string name) { if (type.TryGetCustomAttribute(out DebugNameAttribute atr)) { name = atr.name; return true; } name = string.Empty; return false; } #endregion #region GetGroup public static DebugGroup GetGroup() => GetGroup(typeof(T)); public static DebugGroup GetGroup(Type type) => type.TryGetCustomAttribute(out DebugGroupAttribute atr) ? atr.GetData() : DebugGroup.Empty; public static bool TryGetGroup(out DebugGroup text) => TryGetGroup(typeof(T), out text); public static bool TryGetGroup(Type type, out DebugGroup group) { if (type.TryGetCustomAttribute(out DebugGroupAttribute atr)) { group = atr.GetData(); return true; } group = DebugGroup.Empty; return false; } #endregion #region GetDescription public static string GetDescription() => GetDescription(typeof(T)); public static string GetDescription(Type type) => type.TryGetCustomAttribute(out DebugDescriptionAttribute atr) ? atr.description : string.Empty; public static bool TryGetDescription(out string text) => TryGetDescription(typeof(T), out text); public static bool TryGetDescription(Type type, out string text) { if (type.TryGetCustomAttribute(out DebugDescriptionAttribute atr)) { text = atr.description; return true; } text = string.Empty; return false; } #endregion #region GetColor private static Random random = new Random(); private static Dictionary _words = new Dictionary(); private class WordColor { public int wordsCount; public DebugColor color; } private class NameColor { public List colors = new List(); public NameColor(IEnumerable nameWords) { foreach (var word in nameWords) { if (!_words.TryGetValue(word, out WordColor color)) { color = new WordColor(); _words.Add(word, color); color.color = new DebugColor((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2; } color.wordsCount++; colors.Add(color); } } private int CalcTotalWordsColor() { int result = 0; for (int i = 0, iMax = colors.Count; i < iMax; i++) { result += colors[i].wordsCount; } return result; } public DebugColor CalcColor() { float r = 0, g = 0, b = 0; int totalWordsCount = CalcTotalWordsColor(); for (int i = 0, iMax = colors.Count; i < iMax; i++) { var color = colors[i]; float m = (float)color.wordsCount / totalWordsCount; r += m * color.color.r; g += m * color.color.g; b += m * color.color.b; } return new DebugColor((byte)r, (byte)g, (byte)b); } } private static Dictionary _names = new Dictionary(); private static DebugColor CalcNameColorFor(Type type) { Type targetType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; if (!_names.TryGetValue(targetType, out NameColor nameColor)) { nameColor = new NameColor(SplitString(targetType.Name)); _names.Add(targetType, nameColor); } return nameColor.CalcColor(); } public static List SplitString(string s) { string subs; List words = new List(); int start = 0; for (int i = 1; i < s.Length; i++) { if (char.IsUpper(s[i])) { subs = s.Substring(start, i - start); if (subs.Length > 2 && subs.ToLower() != "system") words.Add(subs); start = i; } } subs = s.Substring(start); if (subs.Length > 2 && subs.ToLower() != "system") words.Add(subs); return words; } public static DebugColor GetColor() => GetColor(typeof(T)); public static DebugColor GetColor(Type type) { var atr = type.GetCustomAttribute(); return atr != null ? atr.color #if DEBUG //optimization for release build : CalcNameColorFor(type); #else : DebugColor.BlackColor; #endif } public static bool TryGetColor(out DebugColor color) => TryGetColor(typeof(T), out color); public static bool TryGetColor(Type type, out DebugColor color) { var atr = type.GetCustomAttribute(); if (atr != null) { color = atr.color; return true; } color = DebugColor.BlackColor; return false; } #endregion #region IsHidden public static bool IsHidden() => IsHidden(typeof(T)); public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out DebugHideAttribute _); #endregion #region GenerateTypeDebugData public static TypeDebugData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); public static TypeDebugData GenerateTypeDebugData(Type type) { return new TypeDebugData( type, GetName(type), GetGroup(type), GetColor(type), GetDescription(type), IsHidden(type)); } #endregion #region ReflectionExtensions internal static bool TryGetCustomAttribute(this Type self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); return attribute != null; } internal static bool TryGetCustomAttribute(this MemberInfo self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); return attribute != null; } #endregion } [Serializable] public sealed class TypeDebugData { public readonly Type type; public readonly string name; public readonly DebugGroup group; public readonly DebugColor color; public readonly string description; public readonly bool isHidden; public TypeDebugData(Type type, string name, DebugGroup group, DebugColor color, string description, bool isHidden) { this.type = type; this.name = name; this.group = group; this.color = color; this.description = description; this.isHidden = isHidden; } } }