diff --git a/src/Dhgms.Nucleotide.GenerationTests/Dhgms.Nucleotide.SampleApp.csproj b/src/Dhgms.Nucleotide.GenerationTests/Dhgms.Nucleotide.SampleApp.csproj index 4fef7e23..dbfa9e64 100644 --- a/src/Dhgms.Nucleotide.GenerationTests/Dhgms.Nucleotide.SampleApp.csproj +++ b/src/Dhgms.Nucleotide.GenerationTests/Dhgms.Nucleotide.SampleApp.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Dhgms.Nucleotide.Generators/CompilerServices/IsExternalInit.cs b/src/Dhgms.Nucleotide.Generators/CompilerServices/IsExternalInit.cs new file mode 100644 index 00000000..051db51a --- /dev/null +++ b/src/Dhgms.Nucleotide.Generators/CompilerServices/IsExternalInit.cs @@ -0,0 +1,13 @@ +using System.ComponentModel; + +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices +{ + /// + /// Enables support for C# 9/10 records on older frameworks. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} diff --git a/src/Dhgms.Nucleotide.Generators/Dhgms.Nucleotide.Generators.csproj b/src/Dhgms.Nucleotide.Generators/Dhgms.Nucleotide.Generators.csproj index cf6fd3a3..55b7eb49 100644 --- a/src/Dhgms.Nucleotide.Generators/Dhgms.Nucleotide.Generators.csproj +++ b/src/Dhgms.Nucleotide.Generators/Dhgms.Nucleotide.Generators.csproj @@ -2,7 +2,7 @@ netstandard2.0 True - 9 + 12 full True diff --git a/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelClassGeneratorProcessor.cs b/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelClassGeneratorProcessor.cs index 91d1785e..d73fbd18 100644 --- a/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelClassGeneratorProcessor.cs +++ b/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelClassGeneratorProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Dhgms.Nucleotide.Generators.GeneratorProcessors; using Dhgms.Nucleotide.Generators.Models; using Dhgms.Nucleotide.Generators.PropertyInfo; @@ -27,7 +28,85 @@ public class UnkeyedModelClassGeneratorProcessor : BaseClassLevelCodeGeneratorPr protected override IEnumerable GetPropertyDeclarations(IEntityGenerationModel entityGenerationModel) { - return entityGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray(); + var result = new List(); + + return GetPropertyDeclarations(entityGenerationModel, result); + } + + private IEnumerable GetPropertyDeclarations(IEntityGenerationModel entityGenerationModel, List result) + { + var properties = entityGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray(); + if (properties != null) + { + result.AddRange(properties); + } + + if (entityGenerationModel.InterfaceGenerationModels != null) + { + foreach (var interfaceGenerationModel in entityGenerationModel.InterfaceGenerationModels) + { + DoPropertyDeclarations(interfaceGenerationModel, result); + } + } + + return result; + } + + private void DoPropertyDeclarations(InterfaceGenerationModel interfaceGenerationModel, List result) + { + PropertyDeclarationSyntax[] properties; + if (interfaceGenerationModel.Properties != null) + { + properties = interfaceGenerationModel.Properties?.Select(GetPropertyDeclaration).ToArray(); + if (properties != null) + { + result.AddRange(properties); + } + } + + if (interfaceGenerationModel.BaseInterfaces == null) + { + return; + } + + foreach (var baseInterface in interfaceGenerationModel.BaseInterfaces) + { + DoPropertyDeclarations(baseInterface, result); + } + } + + private PropertyDeclarationSyntax GetPropertyDeclaration(PropertyGenerationModel propertyGenerationModel) + { + var type = SyntaxFactory.ParseName(propertyGenerationModel.TypeName); + var identifier = propertyGenerationModel.Name; + + var summary = new[] + { + SyntaxFactory.Comment($"/// "), + }; + + var accessorList = GetPropertyAccessorDeclarationSyntaxCollection(propertyGenerationModel.PropertyAccessorFlags); + + var result = SyntaxFactory.PropertyDeclaration(type, identifier) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithAccessorList( + SyntaxFactory.AccessorList( + SyntaxFactory.List(accessorList) + )) + .WithLeadingTrivia(summary); + return result; + } + + private static AccessorDeclarationSyntax[] GetPropertyAccessorDeclarationSyntaxCollection( + PropertyAccessorFlags propertyAccessorFlags) + { + return + [ + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)) + ]; } protected override PropertyDeclarationSyntax GetPropertyDeclaration(PropertyInfoBase propertyInfo, AccessorDeclarationSyntax[] accessorList, IEnumerable summary) @@ -37,7 +116,7 @@ protected override PropertyDeclarationSyntax GetPropertyDeclaration(PropertyInfo var attributes = GetAttributesForProperty(propertyInfo); var result = SyntaxFactory.PropertyDeclaration(type, identifier) - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.VirtualKeyword)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) .WithAccessorList( SyntaxFactory.AccessorList( SyntaxFactory.List(accessorList) @@ -59,29 +138,6 @@ protected override IList GetUsings() return null; } - private MemberDeclarationSyntax[] GetUnkeyedClasses() - { - var name = "Test"; - - var leadingTrivia = new[] - { - SyntaxFactory.Comment($"/// Represents the Unkeyed {name} model. Typically used for adding a new record."), - }; - - var baseTypes = new BaseTypeSyntax[] - { - SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"IUnkeyed{name}Model")) - }; - - return new MemberDeclarationSyntax[] - { - SyntaxFactory.ClassDeclaration($"Unkeyed{name}Model") - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) - .AddBaseListTypes(baseTypes) - .WithLeadingTrivia(leadingTrivia) - }; - } - protected override string[] GetClassPrefixes() => new [] {"Unkeyed"}; protected override string GetClassSuffix() => "Model"; diff --git a/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelInterfaceGeneratorProcessor.cs b/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelInterfaceGeneratorProcessor.cs index b2c6525f..303ce20f 100644 --- a/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelInterfaceGeneratorProcessor.cs +++ b/src/Dhgms.Nucleotide.Generators/Features/Model/UnkeyedModelInterfaceGeneratorProcessor.cs @@ -42,7 +42,8 @@ protected override MethodDeclarationSyntax[] GetMethodDeclarations(string classN protected override string[] GetBaseInterfaces(IEntityGenerationModel entityGenerationModel, string prefix) { - return null; + return entityGenerationModel.InterfaceGenerationModels?.Select(x => x.ClassName) + .ToArray(); } } } \ No newline at end of file diff --git a/src/Dhgms.Nucleotide.Generators/Models/EntityGenerationModel.cs b/src/Dhgms.Nucleotide.Generators/Models/EntityGenerationModel.cs index f002829c..e339ca99 100644 --- a/src/Dhgms.Nucleotide.Generators/Models/EntityGenerationModel.cs +++ b/src/Dhgms.Nucleotide.Generators/Models/EntityGenerationModel.cs @@ -20,7 +20,7 @@ public abstract class EntityGenerationModel : IEntityGenerationModel public abstract IEntityGenerationModel BaseTypeEntityGenerationModel { get; } - public abstract IInterfaceGenerationModel[] InterfaceGenerationModels { get; } + public abstract InterfaceGenerationModel[] InterfaceGenerationModels { get; } /// /// Gets the name of the information class. diff --git a/src/Dhgms.Nucleotide.Generators/Models/IEntityGenerationModel.cs b/src/Dhgms.Nucleotide.Generators/Models/IEntityGenerationModel.cs index b1204723..549b7919 100644 --- a/src/Dhgms.Nucleotide.Generators/Models/IEntityGenerationModel.cs +++ b/src/Dhgms.Nucleotide.Generators/Models/IEntityGenerationModel.cs @@ -23,7 +23,7 @@ public interface IEntityGenerationModel : IObjectGenerationModel IEntityGenerationModel BaseTypeEntityGenerationModel { get; } - IInterfaceGenerationModel[] InterfaceGenerationModels { get; } + InterfaceGenerationModel[] InterfaceGenerationModels { get; } /// /// Gets the class remarks. diff --git a/src/Dhgms.Nucleotide.Generators/Models/IInterfaceGenerationModel.cs b/src/Dhgms.Nucleotide.Generators/Models/IInterfaceGenerationModel.cs index 6b38775d..f17faddf 100644 --- a/src/Dhgms.Nucleotide.Generators/Models/IInterfaceGenerationModel.cs +++ b/src/Dhgms.Nucleotide.Generators/Models/IInterfaceGenerationModel.cs @@ -6,12 +6,18 @@ namespace Dhgms.Nucleotide.Generators.Models { - public interface IInterfaceGenerationModel : IObjectGenerationModel + /// + /// Represents the model for an interface. + /// + /// Name of the interface. + /// Properties directly defined on the interface. + /// Methods directly defined on the interface. + /// Interfaces that this interface inherits from. + public record InterfaceGenerationModel( + string ClassName, + IList Properties, + IList Methods, + IList BaseInterfaces) : IObjectGenerationModel { - IList Properties { get; } - - IList Methods { get; } - - IList BaseInterfaces { get; } } } \ No newline at end of file diff --git a/src/Dhgms.Nucleotide.Generators/Models/IPropertyGenerationModel.cs b/src/Dhgms.Nucleotide.Generators/Models/IPropertyGenerationModel.cs index f477c879..5f4a5c9f 100644 --- a/src/Dhgms.Nucleotide.Generators/Models/IPropertyGenerationModel.cs +++ b/src/Dhgms.Nucleotide.Generators/Models/IPropertyGenerationModel.cs @@ -4,10 +4,14 @@ namespace Dhgms.Nucleotide.Generators.Models { - public interface IPropertyGenerationModel : INameable - { - string Type { get; } - - PropertyAccessorFlags PropertyAccessorFlags { get; } - } + /// + /// Represents a property on an object. + /// + /// The fully qualified name of the type. + /// Accessor flags for the property. + public record PropertyGenerationModel( + string TypeName, + string Name, + PropertyAccessorFlags PropertyAccessorFlags) + : INameable; } diff --git a/src/Dhgms.Nucleotide.ModelTests/Dhgms.Nucleotide.SampleGenerator.csproj b/src/Dhgms.Nucleotide.ModelTests/Dhgms.Nucleotide.SampleGenerator.csproj index c6892889..930f22fd 100644 --- a/src/Dhgms.Nucleotide.ModelTests/Dhgms.Nucleotide.SampleGenerator.csproj +++ b/src/Dhgms.Nucleotide.ModelTests/Dhgms.Nucleotide.SampleGenerator.csproj @@ -4,6 +4,10 @@ netstandard2.0 false true + True + 12 + full + True diff --git a/src/Dhgms.Nucleotide.ModelTests/InterfaceGenerationModels/Whipstaff/Entities/NameableInterfaceGenerationModel.cs b/src/Dhgms.Nucleotide.ModelTests/InterfaceGenerationModels/Whipstaff/Entities/NameableInterfaceGenerationModel.cs new file mode 100644 index 00000000..da9eb094 --- /dev/null +++ b/src/Dhgms.Nucleotide.ModelTests/InterfaceGenerationModels/Whipstaff/Entities/NameableInterfaceGenerationModel.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Dhgms.Nucleotide.Generators.Models; + +namespace Dhgms.Nucleotide.SampleGenerator.InterfaceGenerationModels.Whipstaff.Entities +{ + public sealed record NameableInterfaceGenerationModel() : InterfaceGenerationModel( + "Whipstaff.Core.Entities.INameable", + new List + { + new ("string", "Name", PropertyAccessorFlags.Get) + }, + new List(), + new List()); +} diff --git a/src/Dhgms.Nucleotide.ModelTests/ModelGenerationDetails.cs b/src/Dhgms.Nucleotide.ModelTests/ModelGenerationDetails.cs index 43925550..bf30e99f 100644 --- a/src/Dhgms.Nucleotide.ModelTests/ModelGenerationDetails.cs +++ b/src/Dhgms.Nucleotide.ModelTests/ModelGenerationDetails.cs @@ -2,12 +2,14 @@ // DHGMS Solutions and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using System.Collections.Generic; using Dhgms.Nucleotide.Generators.Features.Database; using Dhgms.Nucleotide.Generators.Features.EntityFramework; using Dhgms.Nucleotide.Generators.Features.ReactiveUI.Wpf; using Dhgms.Nucleotide.Generators.Models; using Dhgms.Nucleotide.Generators.PropertyInfo; +using Dhgms.Nucleotide.SampleGenerator.InterfaceGenerationModels.Whipstaff.Entities; namespace Dhgms.Nucleotide.ModelTests { @@ -17,7 +19,7 @@ public class AddressEntityGenerationModel : EntityGenerationModel public override KeyType KeyType => KeyType.Int32; public override IEntityGenerationModel BaseTypeEntityGenerationModel => null; - public override IInterfaceGenerationModel[] InterfaceGenerationModels => null; + public override InterfaceGenerationModel[] InterfaceGenerationModels => null; public override string ClassRemarks => "Represents an Address"; @@ -32,7 +34,7 @@ public class GenderEntityGenerationModel : EntityGenerationModel public override KeyType KeyType => KeyType.Int32; public override IEntityGenerationModel BaseTypeEntityGenerationModel => null; - public override IInterfaceGenerationModel[] InterfaceGenerationModels => null; + public override InterfaceGenerationModel[] InterfaceGenerationModels => null; public override string ClassRemarks => "Represents a Gender"; @@ -48,7 +50,7 @@ public class PersonEntityGenerationModel : EntityGenerationModel public override KeyType KeyType => KeyType.Int32; public override IEntityGenerationModel BaseTypeEntityGenerationModel => null; - public override IInterfaceGenerationModel[] InterfaceGenerationModels => null; + public override InterfaceGenerationModel[] InterfaceGenerationModels => null; public override string ClassRemarks => "Represents a Person"; @@ -63,14 +65,14 @@ public class SalutationEntityGenerationModel : EntityGenerationModel public override KeyType KeyType => KeyType.Int32; public override IEntityGenerationModel BaseTypeEntityGenerationModel => null; - public override IInterfaceGenerationModel[] InterfaceGenerationModels => null; + public override InterfaceGenerationModel[] InterfaceGenerationModels => new InterfaceGenerationModel[] + { + new NameableInterfaceGenerationModel() + }; public override string ClassRemarks => "Represents a Salutation"; - public override PropertyInfoBase[] Properties => new PropertyInfoBase[] - { - new ClrStringPropertyInfo(CollectionType.None, "Name", "Name of the salutation", false, 3, 255, false, false, null), - }; + public override PropertyInfoBase[] Properties => Array.Empty(); } public class UserEntityGenerationModel : EntityGenerationModel @@ -79,7 +81,7 @@ public class UserEntityGenerationModel : EntityGenerationModel public override KeyType KeyType => KeyType.Int32; public override IEntityGenerationModel BaseTypeEntityGenerationModel => null; - public override IInterfaceGenerationModel[] InterfaceGenerationModels => null; + public override InterfaceGenerationModel[] InterfaceGenerationModels => null; public override string ClassRemarks => "Represents a User";