2023-05-23 01:47:28 +08:00
using System ;
2023-06-16 11:53:59 +08:00
using System.Collections.Generic ;
2023-05-23 01:47:28 +08:00
using System.Reflection ;
namespace DCFApixels.DragonECS
{
public static class EcsDebugUtility
{
2023-06-16 12:34:54 +08:00
#region GetGenericTypeName
2023-05-26 04:25:09 +08:00
public static string GetGenericTypeFullName < T > ( int maxDepth = 2 ) = > GetGenericTypeFullName ( typeof ( T ) , maxDepth ) ;
public static string GetGenericTypeFullName ( Type type , int maxDepth = 2 ) = > GetGenericTypeNameInternal ( type , maxDepth , true ) ;
2023-05-23 01:47:28 +08:00
public static string GetGenericTypeName < T > ( int maxDepth = 2 ) = > GetGenericTypeName ( typeof ( T ) , maxDepth ) ;
2023-05-26 04:25:09 +08:00
public static string GetGenericTypeName ( Type type , int maxDepth = 2 ) = > GetGenericTypeNameInternal ( type , maxDepth , false ) ;
private static string GetGenericTypeNameInternal ( Type type , int maxDepth , bool isFull )
2023-05-23 01:47:28 +08:00
{
#if ( DEBUG & & ! DISABLE_DEBUG )
2023-06-16 12:34:54 +08:00
string result = isFull ? type . FullName : type . Name ;
2023-05-23 01:47:28 +08:00
if ( ! type . IsGenericType | | maxDepth = = 0 )
2023-06-16 12:34:54 +08:00
return result ;
2023-05-23 01:47:28 +08:00
2023-06-16 12:34:54 +08:00
int iBacktick = result . IndexOf ( '`' ) ;
2023-05-23 01:47:28 +08:00
if ( iBacktick > 0 )
2023-06-16 12:34:54 +08:00
result = result . Remove ( iBacktick ) ;
2023-05-23 01:47:28 +08:00
2023-06-16 12:34:54 +08:00
result + = "<" ;
2023-05-23 01:47:28 +08:00
Type [ ] typeParameters = type . GetGenericArguments ( ) ;
for ( int i = 0 ; i < typeParameters . Length ; + + i )
{
2023-05-26 04:25:09 +08:00
string typeParamName = GetGenericTypeNameInternal ( typeParameters [ i ] , maxDepth - 1 , false ) ; //чтобы строка не была слишком длинной, используются сокращенные имена для типов аргументов
2023-06-16 12:34:54 +08:00
result + = ( i = = 0 ? typeParamName : "," + typeParamName ) ;
2023-05-23 01:47:28 +08:00
}
2023-06-16 12:34:54 +08:00
result + = ">" ;
return result ;
2023-05-23 02:11:00 +08:00
#else //optimization for release build
2023-05-23 01:47:28 +08:00
return type . Name ;
#endif
}
2023-06-16 12:34:54 +08:00
#endregion
2023-05-23 01:47:28 +08:00
2023-06-22 10:50:47 +08:00
#region AutoToString
2023-06-22 10:58:57 +08:00
/// <summary> slow but automatic conversion of ValueType to string in the format "name(field1, field2... fieldn)" </summary>
2023-06-22 10:50:47 +08:00
public static string AutoToString < T > ( this T self , bool isWriteName = true ) where T : struct
{
return AutoToString ( self , typeof ( T ) , isWriteName ) ;
}
2023-06-22 10:58:57 +08:00
private static string AutoToString ( object target , Type type , bool isWriteName )
2023-06-22 10:50:47 +08:00
{
var fields = type . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ;
string [ ] values = new string [ fields . Length ] ;
for ( int i = 0 ; i < fields . Length ; i + + )
2023-06-22 11:00:33 +08:00
values [ i ] = ( fields [ i ] . GetValue ( target ) ? ? "NULL" ) . ToString ( ) ;
2023-06-22 10:58:57 +08:00
if ( isWriteName )
2023-06-22 10:50:47 +08:00
return $"{type.Name}({string.Join(" , ", values)})" ;
else
return $"({string.Join(" , ", values)})" ;
}
#endregion
2023-06-16 12:34:54 +08:00
#region GetName
2023-05-23 01:47:28 +08:00
public static string GetName < T > ( ) = > GetName ( typeof ( T ) ) ;
2023-06-16 12:34:54 +08:00
public static string GetName ( Type type ) = > type . TryGetCustomAttribute ( out DebugNameAttribute atr ) ? atr . name : GetGenericTypeName ( type ) ;
2023-06-16 11:53:59 +08:00
public static bool TryGetCustomName < T > ( out string name ) = > TryGetCustomName ( typeof ( T ) , out name ) ;
public static bool TryGetCustomName ( Type type , out string name )
{
2023-06-16 12:34:54 +08:00
if ( type . TryGetCustomAttribute ( out DebugNameAttribute atr ) )
2023-06-16 11:53:59 +08:00
{
name = atr . name ;
return true ;
}
name = string . Empty ;
return false ;
}
2023-06-16 12:34:54 +08:00
#endregion
2023-05-23 01:47:28 +08:00
2023-06-29 17:57:28 +08:00
#region GetGroup
public static DebugGroup GetGroup < T > ( ) = > GetGroup ( typeof ( T ) ) ;
public static DebugGroup GetGroup ( Type type ) = > type . TryGetCustomAttribute ( out DebugGroupAttribute atr ) ? atr . GetData ( ) : DebugGroup . Empty ;
public static bool TryGetGroup < T > ( 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
2023-06-16 12:34:54 +08:00
#region GetDescription
2023-05-23 01:47:28 +08:00
public static string GetDescription < T > ( ) = > GetDescription ( typeof ( T ) ) ;
2023-06-16 12:34:54 +08:00
public static string GetDescription ( Type type ) = > type . TryGetCustomAttribute ( out DebugDescriptionAttribute atr ) ? atr . description : string . Empty ;
2023-06-16 11:53:59 +08:00
public static bool TryGetDescription < T > ( out string text ) = > TryGetDescription ( typeof ( T ) , out text ) ;
public static bool TryGetDescription ( Type type , out string text )
{
2023-06-16 12:34:54 +08:00
if ( type . TryGetCustomAttribute ( out DebugDescriptionAttribute atr ) )
2023-06-16 11:53:59 +08:00
{
text = atr . description ;
return true ;
}
text = string . Empty ;
return false ;
}
2023-06-16 12:34:54 +08:00
#endregion
2023-06-16 11:53:59 +08:00
#region GetColor
private static Random random = new Random ( ) ;
private static Dictionary < string , WordColor > _words = new Dictionary < string , WordColor > ( ) ;
private class WordColor
{
public int wordsCount ;
2023-06-29 17:57:28 +08:00
public DebugColor color ;
2023-06-16 11:53:59 +08:00
}
private class NameColor
{
public List < WordColor > colors = new List < WordColor > ( ) ;
public NameColor ( IEnumerable < string > nameWords )
{
foreach ( var word in nameWords )
{
2023-06-22 14:39:12 +08:00
if ( ! _words . TryGetValue ( word , out WordColor color ) )
2023-06-16 11:53:59 +08:00
{
color = new WordColor ( ) ;
_words . Add ( word , color ) ;
2023-06-29 17:57:28 +08:00
color . color = new DebugColor ( ( byte ) random . Next ( ) , ( byte ) random . Next ( ) , ( byte ) random . Next ( ) ) . UpContrastColor ( ) / 2 ;
2023-06-16 11:53:59 +08:00
}
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 ;
}
2023-06-29 17:57:28 +08:00
public DebugColor CalcColor ( )
2023-06-16 11:53:59 +08:00
{
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 ;
}
2023-06-29 17:57:28 +08:00
return new DebugColor ( ( byte ) r , ( byte ) g , ( byte ) b ) ;
2023-06-16 11:53:59 +08:00
}
}
private static Dictionary < Type , NameColor > _names = new Dictionary < Type , NameColor > ( ) ;
2023-06-29 17:57:28 +08:00
private static DebugColor CalcNameColorFor ( Type type )
2023-06-16 11:53:59 +08:00
{
Type targetType = type . IsGenericType ? type . GetGenericTypeDefinition ( ) : type ;
2023-06-22 14:39:12 +08:00
if ( ! _names . TryGetValue ( targetType , out NameColor nameColor ) )
2023-06-16 11:53:59 +08:00
{
nameColor = new NameColor ( SplitString ( targetType . Name ) ) ;
_names . Add ( targetType , nameColor ) ;
}
return nameColor . CalcColor ( ) ;
}
public static List < string > SplitString ( string s )
{
string subs ;
List < string > words = new List < string > ( ) ;
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 ;
}
2023-05-23 01:47:28 +08:00
2023-06-29 17:57:28 +08:00
public static DebugColor GetColor < T > ( ) = > GetColor ( typeof ( T ) ) ;
public static DebugColor GetColor ( Type type )
2023-05-23 01:47:28 +08:00
{
var atr = type . GetCustomAttribute < DebugColorAttribute > ( ) ;
2023-06-29 17:57:28 +08:00
return atr ! = null ? atr . color
2023-06-16 11:53:59 +08:00
#if DEBUG //optimization for release build
2023-06-29 17:57:28 +08:00
: CalcNameColorFor ( type ) ;
#else
: DebugColor . BlackColor ;
2023-06-16 11:53:59 +08:00
#endif
}
2023-06-29 17:57:28 +08:00
public static bool TryGetColor < T > ( out DebugColor color ) = > TryGetColor ( typeof ( T ) , out color ) ;
public static bool TryGetColor ( Type type , out DebugColor color )
2023-06-16 11:53:59 +08:00
{
var atr = type . GetCustomAttribute < DebugColorAttribute > ( ) ;
2023-06-22 14:39:12 +08:00
if ( atr ! = null )
2023-06-16 11:53:59 +08:00
{
2023-06-29 17:57:28 +08:00
color = atr . color ;
2023-06-16 11:53:59 +08:00
return true ;
}
2023-06-29 17:57:28 +08:00
color = DebugColor . BlackColor ;
2023-06-16 11:53:59 +08:00
return false ;
2023-05-23 01:47:28 +08:00
}
2023-06-16 11:53:59 +08:00
#endregion
2023-05-31 04:09:55 +08:00
2023-06-16 12:34:54 +08:00
#region IsHidden
2023-05-31 04:09:55 +08:00
public static bool IsHidden < T > ( ) = > IsHidden ( typeof ( T ) ) ;
2023-06-16 12:34:54 +08:00
public static bool IsHidden ( Type type ) = > type . TryGetCustomAttribute ( out DebugHideAttribute _ ) ;
#endregion
2023-06-29 17:57:28 +08:00
#region GenerateTypeDebugData
public static TypeDebugData GenerateTypeDebugData < T > ( ) = > GenerateTypeDebugData ( typeof ( T ) ) ;
public static TypeDebugData GenerateTypeDebugData ( Type type )
{
return new TypeDebugData (
type ,
GetName ( type ) ,
GetGroup ( type ) ,
GetColor ( type ) ,
GetDescription ( type ) ,
IsHidden ( type ) ) ;
}
#endregion
2023-06-16 12:34:54 +08:00
#region ReflectionExtensions
internal static bool TryGetCustomAttribute < T > ( this Type self , out T attribute ) where T : Attribute
2023-05-31 04:09:55 +08:00
{
2023-06-16 12:34:54 +08:00
attribute = self . GetCustomAttribute < T > ( ) ;
return attribute ! = null ;
2023-05-31 04:09:55 +08:00
}
2023-06-16 12:34:54 +08:00
internal static bool TryGetCustomAttribute < T > ( this MemberInfo self , out T attribute ) where T : Attribute
{
attribute = self . GetCustomAttribute < T > ( ) ;
return attribute ! = null ;
}
#endregion
2023-05-23 01:47:28 +08:00
}
2023-06-29 17:57:28 +08:00
[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 ;
}
}
2023-05-23 01:47:28 +08:00
}