Added documentation to Attribute builder. Added Inheritance methods. Added a base DNA attribute to GenerateTypeDeclarations method.

This commit is contained in:
Samuele Lorefice
2025-02-26 18:58:29 +01:00
parent 1a34d70f8d
commit 55f3d411b3
2 changed files with 115 additions and 2 deletions

View File

@@ -3,23 +3,49 @@ using System.CodeDom;
using System.Collections.Generic; using System.Collections.Generic;
namespace CodeGenerator { namespace CodeGenerator {
// ReSharper disable always BitwiseOperatorOnEnumWithoutFlags
public class AttributeBuilder { public class AttributeBuilder {
private CodeTypeDeclaration _attrDecl = new(); private CodeTypeDeclaration _attrDecl = new();
private List<(Type, string)> _fields = new(); private List<(Type, string)> _fields = new();
/// <summary>
/// List of fields registered for the attribute currently being built.
/// </summary>
/// <remarks>Each tuple contains the type of the field and the name of the field</remarks>
public List<(Type, string)> Fields => _fields;
private List<(Type, string, string)> _properties = new(); private List<(Type, string, string)> _properties = new();
/// <summary>
/// List of properties registered for the attribute currently being built.
/// </summary>
/// <remarks>Each tuple contains the type of the property, the name of the property and the name of the backing field</remarks>
public List<(Type, string, string)> Properties => _properties;
/// <summary>
/// Creates a new instance of the <see cref="AttributeBuilder"/> class.
/// </summary>
public AttributeBuilder() { public AttributeBuilder() {
_attrDecl.IsClass = true; _attrDecl.IsClass = true;
_attrDecl.Attributes = MemberAttributes.Public; _attrDecl.Attributes = MemberAttributes.Public;
_attrDecl.BaseTypes.Add(typeof(Attribute)); _attrDecl.BaseTypes.Add(typeof(Attribute));
} }
/// <summary>
/// Creates a new instance of the <see cref="AttributeBuilder"/> class with the specified name for the Attribute.
/// </summary>
/// <param name="name">Name the built attribute will have.</param>
/// <remarks>The set name can be always overridden by using <see cref="SetName"/> method.</remarks>
public AttributeBuilder(string name) : this() { public AttributeBuilder(string name) : this() {
_attrDecl.Name = name; _attrDecl.Name = name;
} }
/// <summary>
/// Clears the current state of the builder and prepares it for a new attribute declaration.
/// </summary>
/// <remarks>clears the internal metadata for fields and properties, also reinstantiates the <see cref="CodeTypeDeclaration"/> internal instance</remarks>
public AttributeBuilder New() { public AttributeBuilder New() {
_attrDecl = new CodeTypeDeclaration(); _attrDecl = new CodeTypeDeclaration();
_fields.Clear(); _fields.Clear();
@@ -27,11 +53,37 @@ namespace CodeGenerator {
return this; return this;
} }
/// <summary>
/// Sets the base type of the attribute.
/// </summary>
/// <param name="baseType">Fully qualified name from which this is going to be derived</param>
public AttributeBuilder DeriveFromClass(string baseType) {
_attrDecl.BaseTypes[0] = new(baseType);
return this;
}
/// <summary>
/// Adds another base type to the <see cref="CodeTypeDeclaration"/>. To be used only with interfaces.
/// </summary>
/// <param name="interfaceName">Fully qualfied name of the type to add to the BaseTypes list.</param>
public AttributeBuilder Implements(string interfaceName) {
_attrDecl.BaseTypes.Add(interfaceName);
return this;
}
/// <summary>
/// Sets the name of the attribute that will be generated.
/// </summary>
/// <param name="name">Name the attribute will have.</param>
public AttributeBuilder SetName(string name) { public AttributeBuilder SetName(string name) {
_attrDecl.Name = name; _attrDecl.Name = name;
return this; return this;
} }
/// <summary>
/// Sets the <see cref="AttributeUsageAttribute"/> for the attribute being generated.
/// </summary>
/// <param name="usageArgs">List of arguments for <see cref="AttributeUsageAttribute"/></param>
public AttributeBuilder SetAttributeUsage(CodeAttributeArgument usageArgs) { public AttributeBuilder SetAttributeUsage(CodeAttributeArgument usageArgs) {
var attrUsage = new CodeAttributeDeclaration() { var attrUsage = new CodeAttributeDeclaration() {
Name = nameof(AttributeUsageAttribute), Name = nameof(AttributeUsageAttribute),
@@ -41,15 +93,32 @@ namespace CodeGenerator {
return this; return this;
} }
// ReSharper disable always BitwiseOperatorOnEnumWithoutFlags /// <summary>
/// Adds a field to the attribute being generated.
/// </summary>
/// <param name="name">Name of the field to be added.</param>
/// <param name="attributes">Visibility parameters, OR'd sequence of <see cref="MemberAttributes"/></param>
/// <typeparam name="T">type of the attribute</typeparam>
public AttributeBuilder AddField<T>(string name, public AttributeBuilder AddField<T>(string name,
MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) => MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) =>
AddField(typeof(T), name, attributes); AddField(typeof(T), name, attributes);
/// <summary>
/// Adds a field to the attribute being generated.
/// </summary>
/// <param name="type">Type of the attribute. Must be a base type or a fully qualified name</param>
/// <param name="name">Name of the field to be added.</param>
/// <param name="attributes">Visibility parameters, OR'd sequence of <see cref="MemberAttributes"/></param>
public AttributeBuilder AddField(string type, string name, public AttributeBuilder AddField(string type, string name,
MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) => MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) =>
AddField(Type.GetType(type), name, attributes); AddField(Type.GetType(type), name, attributes);
/// <summary>
/// Adds a field to the attribute being generated.
/// </summary>
/// <param name="type">Type of the attribute. Must be a base type or a fully qualified name</param>
/// <param name="name">Name of the field to be added.</param>
/// <param name="attributes">Visibility parameters, OR'd sequence of <see cref="MemberAttributes"/></param>
public AttributeBuilder AddField(Type type, string name, public AttributeBuilder AddField(Type type, string name,
MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) { MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) {
var field = new CodeMemberField(type, name); var field = new CodeMemberField(type, name);
@@ -59,11 +128,29 @@ namespace CodeGenerator {
return this; return this;
} }
/// <summary>
/// Adds an auto-property to the attribute being generated.
/// </summary>
/// <param name="type">Type of the attribute. Must be a base type or a fully qualified name</param>
/// <param name="name">Name of the property to be added.</param>
/// <param name="attributes">Visibility parameters, OR'd sequence of <see cref="MemberAttributes"/></param>
/// <param name="get">Whether the property should have a getter</param>
/// <param name="set">Whether the property should have a setter</param>
/// <remarks>This method is a composite call to <see cref="AddField{T}"/> and <see cref="AddProperty"/></remarks>
public AttributeBuilder AddAutoProperty(Type type, string name, MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) { public AttributeBuilder AddAutoProperty(Type type, string name, MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) {
AddField(type, $"_{name}", MemberAttributes.Private); AddField(type, $"_{name}", MemberAttributes.Private);
return AddProperty(type, name, $"_{name}", attributes, get, set); return AddProperty(type, name, $"_{name}", attributes, get, set);
} }
/// <summary>
/// Adds a property to the attribute being generated.
/// </summary>
/// <param name="type">Type of the attribute. Must be a base type or a fully qualified name</param>
/// <param name="name">Name of the property to be added.</param>
/// <param name="backingPropertyName">Name of the backing field for the property</param>
/// <param name="attributes">Visibility parameters, OR'd sequence of <see cref="MemberAttributes"/></param>
/// <param name="get">Whether the property should have a getter</param>
/// <param name="set">Whether the property should have a setter</param>
public AttributeBuilder AddProperty(Type type, string name, string backingPropertyName, public AttributeBuilder AddProperty(Type type, string name, string backingPropertyName,
MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) { MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) {
var prop = new CodeMemberProperty { var prop = new CodeMemberProperty {
@@ -94,6 +181,9 @@ namespace CodeGenerator {
return this; return this;
} }
/// <summary>
/// Adds a constructor to all the properties inside the attribute being generated.
/// </summary>
public AttributeBuilder AddPropertiesConstructor() { public AttributeBuilder AddPropertiesConstructor() {
var ctor = new CodeConstructor { Attributes = MemberAttributes.Public }; var ctor = new CodeConstructor { Attributes = MemberAttributes.Public };
_attrDecl.Members.Add(ctor); _attrDecl.Members.Add(ctor);
@@ -107,6 +197,10 @@ namespace CodeGenerator {
return this; return this;
} }
/// <summary>
/// Builds the <see cref="CodeTypeDeclaration"/> instance.
/// </summary>
/// <returns>Instance of the <see cref="CodeTypeDeclaration"/> built by the builder.</returns>
public CodeTypeDeclaration Build() => _attrDecl; public CodeTypeDeclaration Build() => _attrDecl;
} }
} }

View File

@@ -21,6 +21,7 @@ namespace CodeGenerator {
private const string OutPath = @"GeneratedOutput"; private const string OutPath = @"GeneratedOutput";
private const string Namespace = "BlendFile"; private const string Namespace = "BlendFile";
private static HashSet<string> _customTypes; private static HashSet<string> _customTypes;
private static readonly string[] ListLenghtStr = {"count", "length", "size"}; private static readonly string[] ListLenghtStr = {"count", "length", "size"};
private static void Log(string message) { private static void Log(string message) {
@@ -142,12 +143,18 @@ namespace CodeGenerator {
var attributeBuilder = new AttributeBuilder(); var attributeBuilder = new AttributeBuilder();
var typeDeclarations = new CodeTypeDeclaration[] { var typeDeclarations = new CodeTypeDeclaration[] {
attributeBuilder.New().SetName("DNAAttribute")
.SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.All")))
.AddAutoProperty(typeof(int), "OriginalIndex")
.AddAutoProperty(typeof(string), "OriginalName")
.AddPropertiesConstructor()
.Build(),
attributeBuilder.New().SetName("DNAFieldAttribute") attributeBuilder.New().SetName("DNAFieldAttribute")
.SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Field"))) .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Field")))
.AddAutoProperty(typeof(int), "Size") .AddAutoProperty(typeof(int), "Size")
.AddAutoProperty(typeof(string), "OriginalType") .AddAutoProperty(typeof(string), "OriginalType")
.AddAutoProperty(typeof(string), "OriginalName")
.AddAutoProperty(typeof(int), "OriginalIndex") .AddAutoProperty(typeof(int), "OriginalIndex")
.AddAutoProperty(typeof(string), "OriginalName")
.AddAutoProperty(typeof(string), "UnderlyingType") .AddAutoProperty(typeof(string), "UnderlyingType")
.AddAutoProperty(typeof(bool), "IsPointer") .AddAutoProperty(typeof(bool), "IsPointer")
.AddAutoProperty(typeof(int), "MemoryOffset") .AddAutoProperty(typeof(int), "MemoryOffset")
@@ -155,9 +162,21 @@ namespace CodeGenerator {
.Build(), .Build(),
attributeBuilder.New().SetName("DNAClassAttribute") attributeBuilder.New().SetName("DNAClassAttribute")
.SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Class | AttributeTargets.Struct"))) .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Class | AttributeTargets.Struct")))
.AddAutoProperty(typeof(int), "Size")
.AddAutoProperty(typeof(int), "OriginalIndex") .AddAutoProperty(typeof(int), "OriginalIndex")
.AddAutoProperty(typeof(string), "OriginalName") .AddAutoProperty(typeof(string), "OriginalName")
.AddPropertiesConstructor()
.Build(),
attributeBuilder.New().SetName("DNAListAttribute")
.SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Property | AttributeTargets.Field")))
.AddAutoProperty(typeof(int), "Size") .AddAutoProperty(typeof(int), "Size")
.AddAutoProperty(typeof(string), "OriginalType")
.AddAutoProperty(typeof(string), "OriginalName")
.AddAutoProperty(typeof(int), "OriginalIndex")
.AddAutoProperty(typeof(string), "UnderlyingType")
.AddAutoProperty(typeof(string), "CountFieldName")
.AddAutoProperty(typeof(int), "CountFieldIndex")
.AddAutoProperty(typeof(int), "MemoryOffset")
.AddPropertiesConstructor() .AddPropertiesConstructor()
.Build() .Build()
}; };