2024-11-01 20:43:15 +08:00
using DCFApixels.DragonECS.Core ;
using DCFApixels.DragonECS.Internal ;
2024-06-05 14:39:19 +08:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED
2024-06-05 14:39:19 +08:00
using System.Reflection ;
2024-08-07 09:45:34 +08:00
#endif
2024-06-05 14:39:19 +08:00
namespace DCFApixels.DragonECS
{
public interface ITypeMeta
{
Type Type { get ; }
string Name { get ; }
MetaColor Color { get ; }
MetaDescription Description { get ; }
MetaGroup Group { get ; }
2024-06-08 02:08:20 +08:00
IReadOnlyList < string > Tags { get ; }
2024-09-14 10:01:48 +08:00
ITypeMeta BaseMeta { get ; }
}
public static class ITypeMetaExstensions
{
public static TypeMeta FindRootTypeMeta ( this ITypeMeta meta )
{
2024-09-27 21:46:16 +08:00
ITypeMeta result = meta ;
while ( result . BaseMeta ! = null ) { result = meta . BaseMeta ; }
2024-09-14 10:01:48 +08:00
return ( TypeMeta ) result ;
}
}
2024-06-13 18:04:18 +08:00
/// <summary> Expanding meta information over Type. </summary>
2024-06-08 02:08:20 +08:00
[MetaColor(MetaColor.DragonRose)]
2024-06-13 18:04:18 +08:00
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.DEBUG_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "Intended for extending meta information of types, for customization of type display in the editor. You can get it by using the object.GetMeta() or Type.ToMeta() extension method. Meta information is collected from meta attributes.")]
2024-10-12 14:48:13 +08:00
[MetaID("248D587C9201EAEA881F27871B4D18A6")]
2024-06-05 14:39:19 +08:00
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public sealed class TypeMeta : ITypeMeta
{
2024-09-27 21:46:16 +08:00
private const string NULL_NAME = "NULL" ;
public static readonly TypeMeta NullTypeMeta ;
private static object _lock = new object ( ) ;
2024-06-05 14:39:19 +08:00
private static readonly Dictionary < Type , TypeMeta > _metaCache = new Dictionary < Type , TypeMeta > ( ) ;
2024-09-27 21:46:16 +08:00
private static int _increment = 1 ;
2024-06-05 14:39:19 +08:00
2024-09-27 21:46:16 +08:00
private readonly int _uniqueID ;
2024-06-05 14:39:19 +08:00
internal readonly Type _type ;
private bool _isCustomName ;
private bool _isCustomColor ;
private bool _isHidden ;
2024-11-01 20:43:15 +08:00
private bool _isObsolete ;
2024-06-05 14:39:19 +08:00
private string _name ;
2024-06-13 18:04:18 +08:00
private string _typeName ;
2024-06-05 14:39:19 +08:00
private MetaColor _color ;
private MetaDescription _description ;
private MetaGroup _group ;
2024-06-08 02:08:20 +08:00
private IReadOnlyList < string > _tags ;
2024-09-16 12:24:52 +08:00
private string _metaID ;
2024-10-02 22:13:10 +08:00
private EcsTypeCode _typeCode ;
2024-06-05 14:39:19 +08:00
private InitFlag _initFlags = InitFlag . None ;
2024-09-27 21:46:16 +08:00
#region Constructors
static TypeMeta ( )
2024-09-16 19:29:48 +08:00
{
2024-09-27 21:46:16 +08:00
NullTypeMeta = new TypeMeta ( typeof ( void ) )
{
_isCustomName = false ,
_isCustomColor = true ,
_isHidden = true ,
_name = NULL_NAME ,
_typeName = NULL_NAME ,
_color = new MetaColor ( MetaColor . Black ) ,
_description = new MetaDescription ( "" , NULL_NAME ) ,
_group = new MetaGroup ( "" ) ,
_tags = Array . Empty < string > ( ) ,
_metaID = string . Empty ,
2024-10-02 22:13:10 +08:00
_typeCode = EcsTypeCodeManager . Get ( typeof ( void ) ) ,
2024-09-27 21:46:16 +08:00
_initFlags = InitFlag . All ,
} ;
_metaCache . Add ( typeof ( void ) , NullTypeMeta ) ;
2024-09-16 19:29:48 +08:00
}
2024-06-05 14:39:19 +08:00
public static TypeMeta Get ( Type type )
{
2024-09-27 21:46:16 +08:00
lock ( _lock ) //TODO посмотреть можно ли тут убрать лок
2024-06-05 14:39:19 +08:00
{
2024-09-09 18:21:21 +08:00
if ( _metaCache . TryGetValue ( type , out TypeMeta result ) = = false )
{
result = new TypeMeta ( type ) ;
_metaCache . Add ( type , result ) ;
}
return result ;
2024-06-05 14:39:19 +08:00
}
}
private TypeMeta ( Type type )
{
2024-09-27 21:46:16 +08:00
_uniqueID = _increment + + ;
2024-06-05 14:39:19 +08:00
_type = type ;
}
#endregion
#region Type
public Type Type
{
get { return _type ; }
}
#endregion
#region Name
private void InitName ( )
{
if ( _initFlags . HasFlag ( InitFlag . Name ) = = false )
{
( _name , _isCustomName ) = MetaGenerator . GetMetaName ( _type ) ;
2024-08-07 09:45:34 +08:00
_typeName = _isCustomName ? MetaGenerator . GetTypeName ( _type ) : _name ;
2024-06-05 14:39:19 +08:00
_initFlags | = InitFlag . Name ;
}
}
public bool IsCustomName
{
get
{
InitName ( ) ;
return _isCustomName ;
}
}
public string Name
{
get
{
InitName ( ) ;
return _name ;
}
}
2024-06-13 18:04:18 +08:00
public string TypeName
{
get
{
InitName ( ) ;
return _typeName ;
}
}
2024-06-05 14:39:19 +08:00
#endregion
#region Color
private void InitColor ( )
{
if ( _initFlags . HasFlag ( InitFlag . Color ) = = false )
{
( _color , _isCustomColor ) = MetaGenerator . GetColor ( _type ) ;
_initFlags | = InitFlag . Color ;
}
}
public bool IsCustomColor
{
get
{
InitColor ( ) ;
return _isCustomColor ;
}
}
public MetaColor Color
{
get
{
InitColor ( ) ;
return _color ;
}
}
#endregion
#region Description
public MetaDescription Description
{
get
{
if ( _initFlags . HasFlag ( InitFlag . Description ) = = false )
{
_description = MetaGenerator . GetDescription ( _type ) ;
_initFlags | = InitFlag . Description ;
}
return _description ;
}
}
#endregion
#region Group
public MetaGroup Group
{
get
{
if ( _initFlags . HasFlag ( InitFlag . Group ) = = false )
{
_group = MetaGenerator . GetGroup ( _type ) ;
_initFlags | = InitFlag . Group ;
}
return _group ;
}
}
#endregion
#region Tags
private void InitTags ( )
{
if ( _initFlags . HasFlag ( InitFlag . Tags ) = = false )
{
_tags = MetaGenerator . GetTags ( _type ) ;
_initFlags | = InitFlag . Tags ;
_isHidden = _tags . Contains ( MetaTags . HIDDEN ) ;
2024-11-01 20:43:15 +08:00
_isObsolete = _tags . Contains ( MetaTags . OBSOLETE ) ;
2024-06-05 14:39:19 +08:00
}
}
2024-06-08 02:08:20 +08:00
public IReadOnlyList < string > Tags
2024-06-05 14:39:19 +08:00
{
get
{
InitTags ( ) ;
return _tags ;
}
}
public bool IsHidden
{
get
{
InitTags ( ) ;
return _isHidden ;
}
}
2024-11-01 20:43:15 +08:00
public bool IsObsolete
{
get
{
InitTags ( ) ;
return _isObsolete ;
}
}
public bool IsHiddenOrObsolete
{
get
{
return IsHidden | | IsObsolete ;
}
}
2024-06-05 14:39:19 +08:00
#endregion
2024-09-16 12:24:52 +08:00
#region MetaID
private void InitMetaID ( )
{
if ( _initFlags . HasFlag ( InitFlag . MetaID ) = = false )
{
_metaID = MetaGenerator . GetMetaID ( _type ) ;
_initFlags | = InitFlag . MetaID ;
}
}
public string MetaID
{
get
{
InitMetaID ( ) ;
return _metaID ;
}
}
#endregion
2024-06-05 14:39:19 +08:00
#region TypeCode
2024-10-02 22:13:10 +08:00
public EcsTypeCode TypeCode
2024-06-05 14:39:19 +08:00
{
get
{
if ( _initFlags . HasFlag ( InitFlag . TypeCode ) = = false )
{
2024-10-02 22:13:10 +08:00
_typeCode = EcsTypeCodeManager . Get ( _type ) ;
2024-06-05 14:39:19 +08:00
_initFlags | = InitFlag . TypeCode ;
}
return _typeCode ;
}
}
#endregion
#region InitializeAll
public TypeMeta InitializeAll ( )
{
if ( _initFlags ! = InitFlag . All )
{
_ = Name ;
_ = Group ;
_ = Color ;
_ = Description ;
_ = Tags ;
2024-09-16 12:24:52 +08:00
_ = MetaID ;
2024-06-05 14:39:19 +08:00
_ = TypeCode ;
}
return this ;
}
#endregion
#region InitFlag
[Flags]
private enum InitFlag : byte
{
None = 0 ,
Name = 1 < < 0 ,
Group = 1 < < 1 ,
Color = 1 < < 2 ,
Description = 1 < < 3 ,
Tags = 1 < < 4 ,
2024-09-16 12:24:52 +08:00
MetaID = 1 < < 5 ,
TypeCode = 1 < < 6 ,
//MemberType = 1 << 7,
2024-06-05 14:39:19 +08:00
2024-09-16 12:24:52 +08:00
All = Name | Group | Color | Description | Tags | TypeCode | MetaID //| MemberType
2024-06-05 14:39:19 +08:00
}
#endregion
#region Other
2024-09-27 21:46:16 +08:00
ITypeMeta ITypeMeta . BaseMeta
2024-06-05 14:39:19 +08:00
{
2024-09-27 21:46:16 +08:00
get { return null ; }
2024-06-05 14:39:19 +08:00
}
2024-09-27 21:46:16 +08:00
private static bool CheckEcsMemener ( Type checkedType )
2024-06-08 02:08:20 +08:00
{
2024-09-27 21:46:16 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED
return checkedType . IsInterface = = false & & checkedType . IsAbstract = = false & & typeof ( IEcsMember ) . IsAssignableFrom ( checkedType ) ;
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(TypeMeta)}.{nameof(CheckEcsMemener)} method does not work." ) ;
return false ;
#endif
2024-06-08 02:08:20 +08:00
}
2024-09-27 21:46:16 +08:00
public static bool IsHasMeta ( Type type )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED
2024-09-30 19:29:19 +08:00
return CheckEcsMemener ( type ) | | Attribute . GetCustomAttributes ( type , typeof ( EcsMetaAttribute ) , false ) . Length > 0 ;
2024-09-27 21:46:16 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMeta)} method does not work." ) ;
return false ;
2024-09-30 19:29:19 +08:00
#endif
}
public static bool IsHasMetaID ( Type type )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED
return type . HasAttribute < MetaIDAttribute > ( ) ;
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMetaID)} method does not work." ) ;
return false ;
2024-09-27 21:46:16 +08:00
#endif
}
public override string ToString ( ) { return Name ; }
/// <returns> Unique ID </returns>
public override int GetHashCode ( ) { return _uniqueID ; }
2024-06-05 14:39:19 +08:00
private class DebuggerProxy : ITypeMeta
{
private readonly TypeMeta _meta ;
2024-09-14 10:01:48 +08:00
2024-09-27 21:46:16 +08:00
public int UniqueID
{
get { return _meta . _uniqueID ; }
}
ITypeMeta ITypeMeta . BaseMeta
2024-09-14 10:01:48 +08:00
{
2024-09-27 21:46:16 +08:00
get { return null ; }
2024-09-14 10:01:48 +08:00
}
2024-06-05 14:39:19 +08:00
public Type Type
{
get { return _meta . Type ; }
}
public string Name
{
get { return _meta . Name ; }
}
public MetaColor Color
{
get { return _meta . Color ; }
}
public MetaDescription Description
{
get { return _meta . Description ; }
}
public MetaGroup Group
{
get { return _meta . Group ; }
}
2024-06-08 02:08:20 +08:00
public IReadOnlyList < string > Tags
2024-06-05 14:39:19 +08:00
{
get { return _meta . Tags ; }
}
2024-09-16 12:24:52 +08:00
public string MetaID
{
get { return _meta . MetaID ; }
}
2024-06-05 14:39:19 +08:00
public DebuggerProxy ( TypeMeta meta )
{
_meta = meta ;
}
}
#endregion
#region MetaGenerator
private static class MetaGenerator
{
private const int GENERIC_NAME_DEPTH = 3 ;
2024-06-11 02:30:00 +08:00
//private static HashSet<Type> _;
2024-08-07 09:45:34 +08:00
//#region GetMemberType
//public static EcsMemberType GetMemberType(Type type)
//{
// throw new NotImplementedException();
//}
//#endregion
2024-06-11 02:30:00 +08:00
2024-06-13 18:04:18 +08:00
#region GetMetaName / GetTypeName
public static string GetTypeName ( Type type )
{
return EcsDebugUtility . GetGenericTypeName ( type , GENERIC_NAME_DEPTH ) ;
}
2024-06-05 14:39:19 +08:00
public static ( string , bool ) GetMetaName ( Type type )
{
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
2024-09-30 19:29:19 +08:00
bool isCustom = type . TryGetAttribute ( out MetaNameAttribute atr ) & & string . IsNullOrEmpty ( atr . name ) = = false ;
2024-06-05 14:39:19 +08:00
if ( isCustom )
{
if ( ( type . IsGenericType & & atr . isHideGeneric = = false ) = = false )
{
return ( atr . name , isCustom ) ;
}
string genericParams = "" ;
Type [ ] typeParameters = type . GetGenericArguments ( ) ;
for ( int i = 0 ; i < typeParameters . Length ; + + i )
{
string paramTypeName = EcsDebugUtility . GetGenericTypeName ( typeParameters [ i ] , GENERIC_NAME_DEPTH ) ;
genericParams + = ( i = = 0 ? paramTypeName : $", {paramTypeName}" ) ;
}
return ( $"{atr.name}<{genericParams}>" , isCustom ) ;
}
return ( EcsDebugUtility . GetGenericTypeName ( type , GENERIC_NAME_DEPTH ) , isCustom ) ;
2024-08-07 09:45:34 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetMetaName)} method does not work." ) ;
return ( type . Name , false ) ;
#endif
2024-06-05 14:39:19 +08:00
}
#endregion
#region GetColor
private static MetaColor AutoColor ( Type type )
{
return new MetaColor ( type . Name ) . UpContrast ( ) ; //.Desaturate(0.48f) / 1.18f;
}
public static ( MetaColor , bool ) GetColor ( Type type )
{
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
2024-09-30 19:29:19 +08:00
bool isCustom = type . TryGetAttribute ( out MetaColorAttribute atr ) ;
2024-06-05 14:39:19 +08:00
return ( isCustom ? atr . color : AutoColor ( type ) , isCustom ) ;
2024-08-07 09:45:34 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetColor)} method does not work." ) ;
return ( AutoColor ( type ) , false ) ;
#endif
2024-06-05 14:39:19 +08:00
}
#endregion
#region GetGroup
public static MetaGroup GetGroup ( Type type )
{
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
2024-09-30 19:29:19 +08:00
return type . TryGetAttribute ( out MetaGroupAttribute atr ) ? atr . Data : MetaGroup . Empty ;
2024-08-07 09:45:34 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetGroup)} method does not work." ) ;
return MetaGroup . Empty ;
#endif
2024-06-05 14:39:19 +08:00
}
#endregion
#region GetDescription
public static MetaDescription GetDescription ( Type type )
{
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
2024-09-30 19:29:19 +08:00
bool isCustom = type . TryGetAttribute ( out MetaDescriptionAttribute atr ) ;
2024-06-05 14:39:19 +08:00
return isCustom ? atr . Data : MetaDescription . Empty ;
2024-08-07 09:45:34 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetDescription)} method does not work." ) ;
return MetaDescription . Empty ;
#endif
2024-06-05 14:39:19 +08:00
}
#endregion
#region GetTags
2024-06-08 02:08:20 +08:00
public static IReadOnlyList < string > GetTags ( Type type )
2024-06-05 14:39:19 +08:00
{
2024-08-07 09:45:34 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
2024-06-05 14:39:19 +08:00
var atr = type . GetCustomAttribute < MetaTagsAttribute > ( ) ;
return atr ! = null ? atr . Tags : Array . Empty < string > ( ) ;
2024-08-07 09:45:34 +08:00
#else
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetTags)} method does not work." ) ;
return Array . Empty < string > ( ) ;
2024-09-16 12:24:52 +08:00
#endif
}
#endregion
#region GetMetaID
2024-09-18 17:02:14 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
private static Dictionary < string , Type > _idTypePairs = new Dictionary < string , Type > ( ) ;
#endif
2024-09-16 12:24:52 +08:00
public static string GetMetaID ( Type type )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ! REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
var atr = type . GetCustomAttribute < MetaIDAttribute > ( ) ;
2024-09-18 17:02:14 +08:00
if ( atr = = null )
{
return string . Empty ;
}
else
{
2024-10-12 20:06:21 +08:00
string id = atr . ID ;
if ( type . IsGenericType & & type . IsGenericTypeDefinition = = false )
2024-09-18 17:02:14 +08:00
{
2024-10-12 20:06:21 +08:00
id = $"{id}<{string.Join(" , ", type.GetGenericArguments().Select(o => GetMetaID(o)))}>" ;
2024-09-18 17:02:14 +08:00
}
2024-10-12 20:06:21 +08:00
if ( _idTypePairs . TryGetValue ( id , out Type otherType ) & & type ! = otherType ) //этот ексепшен не работает, так как атрибуты не кешируются а пересоздаются
{
Throw . Exception ( $"Types {type.ToMeta().TypeName} and {otherType.ToMeta().TypeName} have duplicate {atr.ID} MetaID." ) ;
}
_idTypePairs [ atr . ID ] = type ;
2024-09-18 17:02:14 +08:00
return atr . ID ;
}
2024-09-16 12:24:52 +08:00
#else
2024-09-27 21:46:16 +08:00
EcsDebug . PrintWarning ( $"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetMetaID)} method does not work." ) ;
2024-09-16 12:24:52 +08:00
return string . Empty ;
2024-08-07 09:45:34 +08:00
#endif
2024-06-05 14:39:19 +08:00
}
#endregion
}
2024-08-14 21:23:34 +08:00
#endregion
2024-06-05 14:39:19 +08:00
}
}