282 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Globalization;
 | 
						|
using Mono.Cecil;
 | 
						|
using Mono.Cecil.Cil;
 | 
						|
using Mono.Collections.Generic;
 | 
						|
using UnrealSharpWeaver.Utilities;
 | 
						|
 | 
						|
namespace UnrealSharpWeaver.MetaData;
 | 
						|
 | 
						|
public class BaseMetaData
 | 
						|
{
 | 
						|
    public string Name { get; set; }
 | 
						|
    public Dictionary<string, string> MetaData { get; set; }
 | 
						|
 | 
						|
    // Non-serialized for JSON
 | 
						|
    public readonly string AttributeName;
 | 
						|
    public readonly IMemberDefinition MemberDefinition;
 | 
						|
    public readonly CustomAttribute? BaseAttribute;
 | 
						|
    public readonly string SourceName;
 | 
						|
    // End non-serialized
 | 
						|
 | 
						|
    public BaseMetaData(MemberReference member, string attributeName)
 | 
						|
    {
 | 
						|
        MemberDefinition = member.Resolve();
 | 
						|
        SourceName = MemberDefinition.Name;
 | 
						|
        Name = MemberDefinition.GetEngineName();
 | 
						|
 | 
						|
        AttributeName = attributeName;
 | 
						|
        MetaData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
						|
        BaseAttribute = MemberDefinition.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, AttributeName)!;
 | 
						|
 | 
						|
        AddMetaData();          // Add any [UMetaData("key", "value")] attributes (general metadata attribute to allow support of any engine tag)
 | 
						|
        AddMetaTagsNamespace(); // Add all named attributes in the UnrealSharp.Attributes.MetaTags namespace
 | 
						|
        AddBaseAttributes();    // Add fields from base attribute e.g. [UClass | UFunction | UEnum | UProperty | UStruct]
 | 
						|
        AddDefaultCategory();   // Add Category="Default" if no category yet added
 | 
						|
        AddBlueprintAccess();   // Add default Blueprint access if not already added
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, string value = "")
 | 
						|
    {
 | 
						|
        if (MetaData.TryAdd(key, value))
 | 
						|
        {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        MetaData[key] = value;
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, bool value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value ? "true" : "false");
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, int value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value.ToString());
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, ulong value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value.ToString());
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, float value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value.ToString());
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, double value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value.ToString());
 | 
						|
    }
 | 
						|
 | 
						|
    public void TryAddMetaData(string key, object value)
 | 
						|
    {
 | 
						|
        TryAddMetaData(key, value?.ToString() ?? "");
 | 
						|
    }
 | 
						|
 | 
						|
    public void AddDefaultCategory()
 | 
						|
    {
 | 
						|
        if (!MetaData.ContainsKey("Category"))
 | 
						|
        {
 | 
						|
            TryAddMetaData("Category", "Default");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void AddBlueprintAccess()
 | 
						|
    {
 | 
						|
        if (MetaData.ContainsKey("NotBlueprintType"))
 | 
						|
        {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        TryAddMetaData("BlueprintType", "true");
 | 
						|
        TryAddMetaData("IsBlueprintBase", "true");
 | 
						|
    }
 | 
						|
 | 
						|
    public static ulong GetFlags(IEnumerable<CustomAttribute> customAttributes, string flagsAttributeName)
 | 
						|
    {
 | 
						|
        CustomAttribute? flagsAttribute = customAttributes.FindAttributeByType(WeaverImporter.UnrealSharpNamespace + ".Attributes", flagsAttributeName);
 | 
						|
        return flagsAttribute == null ? 0 : GetFlags(flagsAttribute);
 | 
						|
    }
 | 
						|
 | 
						|
    public static ulong GetFlags(CustomAttribute flagsAttribute)
 | 
						|
    {
 | 
						|
        return (ulong) flagsAttribute.ConstructorArguments[0].Value;
 | 
						|
    }
 | 
						|
 | 
						|
    public static ulong ExtractBoolAsFlags(TypeDefinition attributeType, CustomAttributeNamedArgument namedArg, string flagsAttributeName)
 | 
						|
    {
 | 
						|
        var arg = namedArg.Argument;
 | 
						|
 | 
						|
        if (!(bool)arg.Value)
 | 
						|
        {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        // Find the property definition for this argument to resolve the true value to the desired flags map.
 | 
						|
        var properties = (from prop in attributeType.Properties where prop.Name == namedArg.Name select prop).ToArray();
 | 
						|
        TypeProcessors.ConstructorBuilder.VerifySingleResult(properties, attributeType, "attribute property " + namedArg.Name);
 | 
						|
        return GetFlags(properties[0].CustomAttributes, flagsAttributeName);
 | 
						|
    }
 | 
						|
 | 
						|
    public static ulong ExtractStringAsFlags(TypeDefinition attributeType, CustomAttributeNamedArgument namedArg, string flagsAttributeName)
 | 
						|
    {
 | 
						|
        var arg = namedArg.Argument;
 | 
						|
        var argValue = (string) arg.Value;
 | 
						|
 | 
						|
        if (argValue is not { Length: > 0 })
 | 
						|
        {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        PropertyDefinition? foundProperty = attributeType.FindPropertyByName(namedArg.Name);
 | 
						|
 | 
						|
        if (foundProperty == null)
 | 
						|
        {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        TypeProcessors.ConstructorBuilder.VerifySingleResult([foundProperty], attributeType, "attribute property " + namedArg.Name);
 | 
						|
        return GetFlags(foundProperty.CustomAttributes, flagsAttributeName);
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    public static ulong GetFlags(IMemberDefinition member, string flagsAttributeName)
 | 
						|
    {
 | 
						|
        SequencePoint? sequencePoint = ErrorEmitter.GetSequencePointFromMemberDefinition(member);
 | 
						|
        Collection<CustomAttribute>? customAttributes = member.CustomAttributes;
 | 
						|
        
 | 
						|
        ulong flags = 0;
 | 
						|
 | 
						|
        foreach (CustomAttribute attribute in customAttributes)
 | 
						|
        {
 | 
						|
            TypeDefinition? attributeClass = attribute.AttributeType.Resolve();
 | 
						|
 | 
						|
            if (attributeClass == null)
 | 
						|
            {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            CustomAttribute? flagsMap = attributeClass.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, flagsAttributeName);
 | 
						|
 | 
						|
            if (flagsMap == null)
 | 
						|
            {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            flags |= GetFlags(flagsMap);
 | 
						|
 | 
						|
            if (attribute.HasConstructorArguments)
 | 
						|
            {
 | 
						|
                foreach (CustomAttributeArgument arg in attribute.ConstructorArguments)
 | 
						|
                {
 | 
						|
                    flags |= Convert.ToUInt64(arg.Value);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (!attribute.HasProperties)
 | 
						|
            {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            foreach (CustomAttributeNamedArgument arg in attribute.Properties)
 | 
						|
            {
 | 
						|
                TypeDefinition argType = arg.Argument.Type.Resolve();
 | 
						|
 | 
						|
                if (argType.IsValueType && argType.Namespace == "System" && argType.Name == "Boolean")
 | 
						|
                {
 | 
						|
                    flags |= ExtractBoolAsFlags(attributeClass, arg, flagsAttributeName);
 | 
						|
                }
 | 
						|
                else if (argType.Namespace == "System" && argType.Name == "String")
 | 
						|
                {
 | 
						|
                    flags |= ExtractStringAsFlags(attributeClass, arg, flagsAttributeName);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    throw new InvalidAttributeException(attributeClass, sequencePoint, $"{argType.FullName} is not supported as an attribute property type.");
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return flags;
 | 
						|
    }
 | 
						|
 | 
						|
    private void AddMetaData()
 | 
						|
    {
 | 
						|
        AddMetaData(MemberDefinition);
 | 
						|
    }
 | 
						|
 | 
						|
    protected void AddMetaData(ICustomAttributeProvider provider)
 | 
						|
    {
 | 
						|
        //[UMetaData("key","value")]
 | 
						|
        List<CustomAttribute> metaDataAttributes = provider.CustomAttributes.FindMetaDataAttributes();
 | 
						|
        foreach (var attrib in metaDataAttributes)
 | 
						|
        {
 | 
						|
            switch (attrib.ConstructorArguments.Count)
 | 
						|
            {
 | 
						|
                case < 1:
 | 
						|
                    continue;
 | 
						|
                case 1: 
 | 
						|
                    TryAddMetaData((string)attrib.ConstructorArguments[0].Value);
 | 
						|
                    break;
 | 
						|
                default: 
 | 
						|
                    TryAddMetaData((string)attrib.ConstructorArguments[0].Value, (string)attrib.ConstructorArguments[1].Value);
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private void AddMetaTagsNamespace() 
 | 
						|
    {
 | 
						|
        AddMetaTagsNamespace(MemberDefinition);
 | 
						|
    }
 | 
						|
    
 | 
						|
    protected void AddMetaTagsNamespace(ICustomAttributeProvider provider) 
 | 
						|
    {
 | 
						|
        //Specific MetaData Tags - all attributes in the UnrealSharp.Attributes.MetaTags Namespace
 | 
						|
        List<CustomAttribute> metaDataAttributes = provider.CustomAttributes.FindMetaDataAttributesByNamespace();
 | 
						|
        foreach (var attrib in metaDataAttributes)
 | 
						|
        {
 | 
						|
            var key = attrib.AttributeType.Name.Replace("Attribute", "");
 | 
						|
            if (attrib.HasConstructorArguments)
 | 
						|
            {
 | 
						|
                TryAddMetaData(key, attrib.ConstructorArguments[0].Value);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                TryAddMetaData(key, "true");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private void AddBaseAttributes()
 | 
						|
    {
 | 
						|
        if (BaseAttribute == null)
 | 
						|
        {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        CustomAttributeArgument? displayNameArgument = BaseAttribute.FindAttributeField("DisplayName");
 | 
						|
        if (displayNameArgument.HasValue)
 | 
						|
        {
 | 
						|
            TryAddMetaData("DisplayName", (string) displayNameArgument.Value.Value);
 | 
						|
        }
 | 
						|
 | 
						|
        CustomAttributeArgument? categoryArgument = BaseAttribute.FindAttributeField("Category");
 | 
						|
        if (categoryArgument.HasValue)
 | 
						|
        {
 | 
						|
            TryAddMetaData("Category", (string) categoryArgument.Value.Value);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected bool GetBoolMetadata(string key)
 | 
						|
    {
 | 
						|
        if (!MetaData.TryGetValue(key, out var val))
 | 
						|
        {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        
 | 
						|
        return 0 == StringComparer.OrdinalIgnoreCase.Compare(val, "true");
 | 
						|
    }
 | 
						|
} |