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");
 | |
|     }
 | |
| } |