using System; using System.CodeDom; using System.Collections.Generic; namespace CodeGenerator { // ReSharper disable always BitwiseOperatorOnEnumWithoutFlags public class AttributeBuilder { private CodeTypeDeclaration _attrDecl = new(); private List<(Type, string)> _fields = new(); /// /// List of fields registered for the attribute currently being built. /// /// Each tuple contains the type of the field and the name of the field public List<(Type, string)> Fields => _fields; private List<(Type, string, string)> _properties = new(); /// /// List of properties registered for the attribute currently being built. /// /// Each tuple contains the type of the property, the name of the property and the name of the backing field public List<(Type, string, string)> Properties => _properties; /// /// Creates a new instance of the class. /// public AttributeBuilder() { _attrDecl.IsClass = true; _attrDecl.Attributes = MemberAttributes.Public; _attrDecl.BaseTypes.Add(typeof(Attribute)); } /// /// Creates a new instance of the class with the specified name for the Attribute. /// /// Name the built attribute will have. /// The set name can be always overridden by using method. public AttributeBuilder(string name) : this() { _attrDecl.Name = name; } /// /// Clears the current state of the builder and prepares it for a new attribute declaration. /// /// clears the internal metadata for fields and properties, also reinstantiates the internal instance public AttributeBuilder New() { _attrDecl = new CodeTypeDeclaration(); _fields.Clear(); _properties.Clear(); return this; } /// /// Sets the base type of the attribute. /// /// Fully qualified name from which this is going to be derived public AttributeBuilder DeriveFromClass(string baseType) { if (_attrDecl.BaseTypes.Count == 0) _attrDecl.BaseTypes.Add(typeof(Attribute)); else _attrDecl.BaseTypes[0] = new(baseType); return this; } /// /// Adds another base type to the . To be used only with interfaces. /// /// Fully qualfied name of the type to add to the BaseTypes list. public AttributeBuilder Implements(string interfaceName) { _attrDecl.BaseTypes.Add(interfaceName); return this; } /// /// Sets the name of the attribute that will be generated. /// /// Name the attribute will have. public AttributeBuilder SetName(string name) { _attrDecl.Name = name; return this; } /// /// Sets the for the attribute being generated. /// /// List of arguments for public AttributeBuilder SetAttributeUsage(CodeAttributeArgument usageArgs) { var attrUsage = new CodeAttributeDeclaration() { Name = nameof(AttributeUsageAttribute), Arguments = { usageArgs } }; _attrDecl.CustomAttributes.Add(attrUsage); return this; } /// /// Adds a field to the attribute being generated. /// /// Name of the field to be added. /// Visibility parameters, OR'd sequence of /// type of the attribute public AttributeBuilder AddField(string name, MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) => AddField(typeof(T), name, attributes); /// /// Adds a field to the attribute being generated. /// /// Type of the attribute. Must be a base type or a fully qualified name /// Name of the field to be added. /// Visibility parameters, OR'd sequence of public AttributeBuilder AddField(string type, string name, MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) => AddField(Type.GetType(type), name, attributes); /// /// Adds a field to the attribute being generated. /// /// Type of the attribute. Must be a base type or a fully qualified name /// Name of the field to be added. /// Visibility parameters, OR'd sequence of public AttributeBuilder AddField(Type type, string name, MemberAttributes attributes = MemberAttributes.Private | MemberAttributes.Final) { var field = new CodeMemberField(type, name); field.Attributes = attributes; _attrDecl.Members.Add(field); _fields.Add((type, name)); return this; } /// /// Adds an auto-property to the attribute being generated. /// /// Type of the attribute. Must be a base type or a fully qualified name /// Name of the property to be added. /// Visibility parameters, OR'd sequence of /// Whether the property should have a getter /// Whether the property should have a setter /// This method is a composite call to and public AttributeBuilder AddAutoProperty(Type type, string name, MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) { AddField(type, $"_{name}", MemberAttributes.Private); return AddProperty(type, name, $"_{name}", attributes, get, set); } /// /// Adds a property to the attribute being generated. /// /// Type of the attribute. Must be a base type or a fully qualified name /// Name of the property to be added. /// Name of the backing field for the property /// Visibility parameters, OR'd sequence of /// Whether the property should have a getter /// Whether the property should have a setter public AttributeBuilder AddProperty(Type type, string name, string backingPropertyName, MemberAttributes attributes = MemberAttributes.Public, bool get = true, bool set = true) { var prop = new CodeMemberProperty { Name = name, Type = new (type), Attributes = attributes, HasGet = get, HasSet = set }; if (get) { prop.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), backingPropertyName))); } if (set) { prop.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), backingPropertyName), new CodePropertySetValueReferenceExpression())); } _properties.Add((type, name, backingPropertyName)); _attrDecl.Members.Add(prop); return this; } /// /// Adds a constructor to all the properties inside the attribute being generated. /// public AttributeBuilder AddPropertiesConstructor() { var ctor = new CodeConstructor { Attributes = MemberAttributes.Public }; _attrDecl.Members.Add(ctor); _properties.ForEach(property => { ctor.Parameters.Add(new (property.Item1, property.Item2)); ctor.Statements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), property.Item3), new CodeVariableReferenceExpression(property.Item2))); }); return this; } /// /// Builds the instance. /// /// Instance of the built by the builder. public CodeTypeDeclaration Build() => _attrDecl; } }