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