2024-02-29 02:37:04 +08:00
using System ;
2023-06-16 11:53:59 +08:00
using System.Collections.Generic ;
2023-12-20 23:16:57 +08:00
using System.Linq ;
2023-05-23 01:47:28 +08:00
using System.Reflection ;
2023-10-31 03:03:13 +08:00
using System.Runtime.CompilerServices ;
2023-05-23 01:47:28 +08:00
namespace DCFApixels.DragonECS
{
public static class EcsDebugUtility
{
2023-11-01 02:30:19 +08:00
private const BindingFlags RFL_FLAGS = BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ;
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
{
2024-02-23 18:34:40 +08:00
#if ! REFLECTION_DISABLED
2024-01-18 21:25:26 +08:00
//TODO сделать специальный вывод в виде названий констант для Enum-ов
2024-02-23 18:34:40 +08:00
#pragma warning disable IL2070 // 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The parameter of method does not have matching annotations.
2023-11-01 02:30:19 +08:00
var fields = type . GetFields ( RFL_FLAGS ) ;
2024-02-23 18:34:40 +08:00
#pragma warning restore IL2070
2023-06-22 10:50:47 +08:00
string [ ] values = new string [ fields . Length ] ;
for ( int i = 0 ; i < fields . Length ; i + + )
2024-02-26 11:54:18 +08:00
{
2023-06-22 11:00:33 +08:00
values [ i ] = ( fields [ i ] . GetValue ( target ) ? ? "NULL" ) . ToString ( ) ;
2024-02-26 11:54:18 +08:00
}
2023-06-22 10:58:57 +08:00
if ( isWriteName )
2024-02-26 11:54:18 +08:00
{
2023-06-22 10:50:47 +08:00
return $"{type.Name}({string.Join(" , ", values)})" ;
2024-02-26 11:54:18 +08:00
}
2023-06-22 10:50:47 +08:00
else
2024-02-26 11:54:18 +08:00
{
2023-06-22 10:50:47 +08:00
return $"({string.Join(" , ", values)})" ;
2024-02-26 11:54:18 +08:00
}
2024-02-26 10:43:37 +08:00
#else
2024-02-23 18:34:40 +08:00
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(AutoToString)} method does not work." ) ;
return string . Empty ;
2024-02-26 10:43:37 +08:00
#endif
2023-06-22 10:50:47 +08:00
}
#endregion
2023-06-16 12:34:54 +08:00
#region GetName
2023-11-01 02:30:19 +08:00
public static string GetName ( object obj , int maxGenericDepth = 2 )
2023-10-31 03:03:13 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
GetName ( intr . MetaSource , maxGenericDepth ) :
2023-11-01 02:30:19 +08:00
GetName ( type : obj . GetType ( ) , maxGenericDepth ) ;
2023-10-31 03:03:13 +08:00
}
2023-11-01 02:30:19 +08:00
public static string GetName < T > ( int maxGenericDepth = 2 ) = > GetName ( typeof ( T ) , maxGenericDepth ) ;
2023-12-20 23:16:57 +08:00
public static string GetName ( Type type , int maxGenericDepth = 2 ) = > type . TryGetCustomAttribute ( out MetaNameAttribute atr ) ? atr . name : GetGenericTypeName ( type , maxGenericDepth ) ;
2023-11-01 02:30:19 +08:00
public static bool TryGetCustomName ( object obj , out string name )
2023-10-31 03:03:13 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
TryGetCustomName ( intr . MetaSource , out name ) :
2023-11-01 02:30:19 +08:00
TryGetCustomName ( type : obj . GetType ( ) , out name ) ;
2023-10-31 03:03:13 +08:00
}
2023-11-01 02:30:19 +08:00
public static bool TryGetCustomName < T > ( out string name ) = > TryGetCustomName ( type : typeof ( T ) , out name ) ;
2023-06-16 11:53:59 +08:00
public static bool TryGetCustomName ( Type type , out string name )
{
2023-12-20 23:16:57 +08:00
if ( type . TryGetCustomAttribute ( out MetaNameAttribute 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
2023-12-20 23:16:57 +08:00
public static MetaGroup GetGroup ( object obj )
2023-11-01 02:30:19 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
GetGroup ( intr . MetaSource ) :
2023-11-01 02:30:19 +08:00
GetGroup ( type : obj . GetType ( ) ) ;
}
2023-12-20 23:16:57 +08:00
public static MetaGroup GetGroup < T > ( ) = > GetGroup ( typeof ( T ) ) ;
public static MetaGroup GetGroup ( Type type ) = > type . TryGetCustomAttribute ( out MetaGroupAttribute atr ) ? atr . GetData ( ) : MetaGroup . Empty ;
public static bool TryGetGroup ( object obj , out MetaGroup group )
2023-11-01 02:30:19 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
TryGetGroup ( intr . MetaSource , out group ) :
2023-11-01 02:30:19 +08:00
TryGetGroup ( type : obj . GetType ( ) , out group ) ;
}
2023-12-20 23:16:57 +08:00
public static bool TryGetGroup < T > ( out MetaGroup text ) = > TryGetGroup ( typeof ( T ) , out text ) ;
public static bool TryGetGroup ( Type type , out MetaGroup group )
2023-06-29 17:57:28 +08:00
{
2023-12-20 23:16:57 +08:00
if ( type . TryGetCustomAttribute ( out MetaGroupAttribute atr ) )
2023-06-29 17:57:28 +08:00
{
group = atr . GetData ( ) ;
return true ;
}
2023-12-20 23:16:57 +08:00
group = MetaGroup . Empty ;
2023-06-29 17:57:28 +08:00
return false ;
}
#endregion
2023-06-16 12:34:54 +08:00
#region GetDescription
2023-11-01 02:30:19 +08:00
public static string GetDescription ( object obj )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
GetDescription ( intr . MetaSource ) :
2023-11-01 02:30:19 +08:00
GetDescription ( type : obj . GetType ( ) ) ;
}
2023-05-23 01:47:28 +08:00
public static string GetDescription < T > ( ) = > GetDescription ( typeof ( T ) ) ;
2023-12-20 23:16:57 +08:00
public static string GetDescription ( Type type ) = > type . TryGetCustomAttribute ( out MetaDescriptionAttribute atr ) ? atr . description : string . Empty ;
2023-11-01 02:30:19 +08:00
public static bool TryGetDescription ( object obj , out string text )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
TryGetDescription ( intr . MetaSource , out text ) :
2023-11-01 02:30:19 +08:00
TryGetDescription ( type : obj . GetType ( ) , out text ) ;
}
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-12-20 23:16:57 +08:00
if ( type . TryGetCustomAttribute ( out MetaDescriptionAttribute 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
2023-12-20 23:16:57 +08:00
public static MetaColor GetColor ( object obj )
2023-11-01 02:30:19 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
GetColor ( intr . MetaSource ) :
2023-11-01 02:30:19 +08:00
GetColor ( type : obj . GetType ( ) ) ;
}
2024-02-26 11:54:18 +08:00
public static MetaColor GetColor < T > ( )
{
return GetColor ( typeof ( T ) ) ;
}
2023-12-20 23:16:57 +08:00
public static MetaColor GetColor ( Type type )
2023-05-23 01:47:28 +08:00
{
2023-12-20 23:16:57 +08:00
var atr = type . GetCustomAttribute < MetaColorAttribute > ( ) ;
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
2024-03-02 22:08:32 +08:00
: new MetaColor ( type . Name ) ;
2023-06-29 17:57:28 +08:00
#else
2023-12-20 23:16:57 +08:00
: MetaColor . BlackColor ;
2023-06-16 11:53:59 +08:00
#endif
}
2023-12-20 23:16:57 +08:00
public static bool TryGetColor ( object obj , out MetaColor color )
2023-11-01 02:30:19 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
TryGetColor ( intr . MetaSource , out color ) :
2023-11-01 02:30:19 +08:00
TryGetColor ( type : obj . GetType ( ) , out color ) ;
}
2024-02-26 11:54:18 +08:00
public static bool TryGetColor < T > ( out MetaColor color )
{
return TryGetColor ( typeof ( T ) , out color ) ;
}
2023-12-20 23:16:57 +08:00
public static bool TryGetColor ( Type type , out MetaColor color )
2023-06-16 11:53:59 +08:00
{
2023-12-20 23:16:57 +08:00
var atr = type . GetCustomAttribute < MetaColorAttribute > ( ) ;
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-12-20 23:16:57 +08:00
color = MetaColor . BlackColor ;
return false ;
}
#endregion
#region GetTags
public static IReadOnlyCollection < string > GetTags ( object obj )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
GetTags ( intr . MetaSource ) :
2023-12-20 23:16:57 +08:00
GetTags ( type : obj . GetType ( ) ) ;
}
2024-02-26 11:54:18 +08:00
public static IReadOnlyCollection < string > GetTags < T > ( )
{
return GetTags ( typeof ( T ) ) ;
}
2023-12-20 23:16:57 +08:00
public static IReadOnlyCollection < string > GetTags ( Type type )
{
var atr = type . GetCustomAttribute < MetaTagsAttribute > ( ) ;
return atr ! = null ? atr . Tags : Array . Empty < string > ( ) ;
}
public static bool TryGetTags ( object obj , out IReadOnlyCollection < string > tags )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
TryGetTags ( intr . MetaSource , out tags ) :
2023-12-20 23:16:57 +08:00
TryGetTags ( type : obj . GetType ( ) , out tags ) ;
}
2024-02-26 11:54:18 +08:00
public static bool TryGetTags < T > ( out IReadOnlyCollection < string > tags )
{
return TryGetTags ( typeof ( T ) , out tags ) ;
}
2023-12-20 23:16:57 +08:00
public static bool TryGetTags ( Type type , out IReadOnlyCollection < string > tags )
{
var atr = type . GetCustomAttribute < MetaTagsAttribute > ( ) ;
if ( atr ! = null )
{
tags = atr . Tags ;
return true ;
}
tags = Array . Empty < string > ( ) ;
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-11-01 02:30:19 +08:00
public static bool IsHidden ( object obj )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
IsHidden ( intr . MetaSource ) :
2023-11-01 02:30:19 +08:00
IsHidden ( type : obj . GetType ( ) ) ;
}
2024-02-26 11:54:18 +08:00
public static bool IsHidden < T > ( )
{
return IsHidden ( typeof ( T ) ) ;
}
public static bool IsHidden ( Type type )
{
return type . TryGetCustomAttribute ( out MetaTagsAttribute atr ) & & atr . Tags . Contains ( MetaTags . HIDDEN ) ;
}
2023-06-16 12:34:54 +08:00
#endregion
2023-11-01 02:30:19 +08:00
#region MetaSource
public static bool IsMetaSourceProvided ( object obj )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider ;
2023-11-01 02:30:19 +08:00
}
public static object GetMetaSource ( object obj )
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ? intr . MetaSource : obj ;
2023-11-01 02:30:19 +08:00
}
#endregion
2024-02-26 11:54:18 +08:00
#region GenerateTypeMeta
public static TypeMetaData GenerateTypeMeta ( object obj )
2023-11-01 02:30:19 +08:00
{
2023-12-20 23:31:04 +08:00
return obj is IEcsMetaProvider intr ?
2024-02-26 11:54:18 +08:00
GenerateTypeMeta ( intr . MetaSource ) :
GenerateTypeMeta ( type : obj . GetType ( ) ) ;
2023-11-01 02:30:19 +08:00
}
2024-02-26 11:54:18 +08:00
public static TypeMetaData GenerateTypeMeta < T > ( )
{
return GenerateTypeMeta ( typeof ( T ) ) ;
}
public static TypeMetaData GenerateTypeMeta ( Type type )
2023-06-29 17:57:28 +08:00
{
2023-12-20 23:16:57 +08:00
return new TypeMetaData (
2023-06-29 17:57:28 +08:00
type ,
GetName ( type ) ,
GetGroup ( type ) ,
GetColor ( type ) ,
GetDescription ( type ) ,
2024-02-26 11:54:18 +08:00
GetTags ( type ) ) ;
}
#endregion
#region GetCachedTypeMeta
private static readonly Dictionary < Type , TypeMetaDataCached > _metaCache = new Dictionary < Type , TypeMetaDataCached > ( ) ;
public static TypeMetaDataCached GetCachedTypeMeta ( object obj )
{
return obj is IEcsMetaProvider intr ?
GetCachedTypeMeta ( intr . MetaSource ) :
GetCachedTypeMeta ( type : obj . GetType ( ) ) ;
}
public static TypeMetaDataCached GetCachedTypeMeta < T > ( )
{
return GetCachedTypeMeta ( typeof ( T ) ) ;
}
public static TypeMetaDataCached GetCachedTypeMeta ( Type type )
{
2024-02-26 11:58:19 +08:00
if ( _metaCache . TryGetValue ( type , out TypeMetaDataCached result ) = = false )
2024-02-26 11:54:18 +08:00
{
result = new TypeMetaDataCached ( type ) ;
_metaCache . Add ( type , result ) ;
}
return result ;
2023-06-29 17:57:28 +08:00
}
#endregion
2023-06-16 12:34:54 +08:00
#region ReflectionExtensions
2023-10-31 03:03:13 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-12-31 23:26:56 +08:00
public 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-10-31 03:03:13 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-12-31 23:26:56 +08:00
public static bool TryGetCustomAttribute < T > ( this MemberInfo self , out T attribute ) where T : Attribute
2023-06-16 12:34:54 +08:00
{
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]
2023-12-20 23:16:57 +08:00
public sealed class TypeMetaData
2023-06-29 17:57:28 +08:00
{
public readonly Type type ;
public readonly string name ;
2023-12-20 23:16:57 +08:00
public readonly MetaGroup group ;
public readonly MetaColor color ;
2023-06-29 17:57:28 +08:00
public readonly string description ;
2024-02-26 11:54:18 +08:00
public readonly IReadOnlyCollection < string > tags ;
public TypeMetaData ( Type type , string name , MetaGroup group , MetaColor color , string description , IReadOnlyCollection < string > tags )
2023-06-29 17:57:28 +08:00
{
this . type = type ;
this . name = name ;
this . group = group ;
this . color = color ;
this . description = description ;
2024-02-26 11:58:19 +08:00
this . tags = tags ;
2024-02-26 11:54:18 +08:00
}
}
2024-02-29 02:37:04 +08:00
public static class TypeMetaDataCachedExtensions
{
public static TypeMetaDataCached GetMeta ( this object self )
{
return EcsDebugUtility . GetCachedTypeMeta ( self ) ;
}
}
2024-02-26 11:54:18 +08:00
public class TypeMetaDataCached
{
internal readonly Type _type ;
internal string _name ;
internal MetaGroup _group ;
2024-02-29 02:37:04 +08:00
internal MetaColor _color ;
2024-02-26 11:54:18 +08:00
internal string _description ;
internal IReadOnlyCollection < string > _tags ;
private TypeMetaData _typeMetaData = null ;
private InitFlag _initFlags = InitFlag . None ;
public TypeMetaDataCached ( Type type )
{
_type = type ;
}
public Type Type
{
get { return _type ; }
}
public string Name
{
get
{
2024-02-29 02:37:04 +08:00
if ( _initFlags . HasFlag ( InitFlag . Name ) = = false )
2024-02-26 11:54:18 +08:00
{
_name = EcsDebugUtility . GetName ( _type ) ;
_initFlags | = InitFlag . Name ;
}
return _name ;
}
}
public MetaGroup Group
{
get
{
2024-02-29 02:37:04 +08:00
if ( _initFlags . HasFlag ( InitFlag . Group ) = = false )
2024-02-26 11:54:18 +08:00
{
_group = EcsDebugUtility . GetGroup ( _type ) ;
_initFlags | = InitFlag . Group ;
}
return _group ;
}
}
public MetaColor Color
{
get
{
2024-02-29 02:37:04 +08:00
if ( _initFlags . HasFlag ( InitFlag . Color ) = = false )
2024-02-26 11:54:18 +08:00
{
_color = EcsDebugUtility . GetColor ( _type ) ;
2024-03-02 22:13:44 +08:00
_initFlags | = InitFlag . Color ;
2024-02-26 11:54:18 +08:00
}
2024-02-29 02:37:04 +08:00
return _color ;
2024-02-26 11:54:18 +08:00
}
}
public string Description
{
get
{
2024-02-29 02:37:04 +08:00
if ( _initFlags . HasFlag ( InitFlag . Description ) = = false )
2024-02-26 11:54:18 +08:00
{
_description = EcsDebugUtility . GetDescription ( _type ) ;
_initFlags | = InitFlag . Description ;
}
return _description ;
}
}
public IReadOnlyCollection < string > Tags
{
get
{
2024-02-29 02:37:04 +08:00
if ( _initFlags . HasFlag ( InitFlag . Tags ) = = false )
2024-02-26 11:54:18 +08:00
{
_tags = EcsDebugUtility . GetTags ( _type ) ;
_initFlags | = InitFlag . Tags ;
}
return _tags ;
}
}
public TypeMetaData AllData
{
get
{
2024-02-26 11:58:19 +08:00
if ( _typeMetaData = = null )
2024-02-26 11:54:18 +08:00
{
_typeMetaData = new TypeMetaData (
Type ,
Name ,
Group ,
Color ,
Description ,
Tags ) ;
}
return _typeMetaData ;
}
}
public void InitializeAll ( )
{
if ( _initFlags = = InitFlag . All )
{
return ;
}
_ = Name ;
_ = Group ;
_ = Color ;
_ = Description ;
_ = Tags ;
}
private enum InitFlag : byte
{
None = 0 ,
Name = 1 < < 0 ,
Group = 1 < < 1 ,
Color = 1 < < 2 ,
Description = 1 < < 3 ,
Tags = 1 < < 4 ,
All = Name | Group | Color | Description | Tags
2023-06-29 17:57:28 +08:00
}
}
2024-02-26 11:54:18 +08:00
}