Exemplo n.º 1
0
 private static SourceBuilder GenerateSourceTypeExtensionClass(this SourceBuilder builder, MappingModel model)
 {
     return(builder
            .WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static partial class {model.SourceTypeIdentifierName}To{model.TypeIdentifierName}Extensions")
            .WriteOpeningBracket()
            .GenerateSourceTypeExtensionMethod(model)
            .WriteClosingBracket());
 }
Exemplo n.º 2
0
        private static SourceBuilder GeneratePrivateConstructor(this SourceBuilder builder, MappingModel model)
        {
            var          sourceClassParameterName    = model.SourceTypeIdentifierName.ToCamelCase();
            const string mappingContextParameterName = "context";

            builder
            .WriteLine($"private protected {model.TypeIdentifierName}({MappingContextSource.ClassName} {mappingContextParameterName}, {model.SourceType} {sourceClassParameterName})")
            .Indent()
            .Write(": this(");

            for (var i = 0; i < model.MappedProperties.Length; i++)
            {
                var property = model.MappedProperties[i];
                if (property.TypeConverter is null)
                {
                    if (property.IsEnumerable)
                    {
                        builder.Write(
                            $"{property.Name}: {sourceClassParameterName}.{property.SourcePropertyName}.Select({mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.EnumerableTypeArgument}>).ToList()");
                    }
                    else
                    {
                        builder.Write(property.MappedSourcePropertyTypeName is null
                            ? $"{property.Name}: {sourceClassParameterName}.{property.SourcePropertyName}"
                            : $"{property.Name}: {mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.Type}>({sourceClassParameterName}.{property.SourcePropertyName})");
                    }
                }
                else
                {
                    var parameters = property.TypeConverterParameters.IsEmpty
                        ? "null"
                        : $"new object[] {{ {string.Join(", ", property.TypeConverterParameters)} }}";

                    builder.Write($"{property.Name}: new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.SourcePropertyName}, {parameters})");
                }

                if (i < model.MappedProperties.Length - 1)
                {
                    builder.Write(", ");
                }
            }

            builder.WriteLine(")")
            .Unindent()
            .WriteOpeningBracket()
            .WriteLine($"if ({mappingContextParameterName} == null) throw new ArgumentNullException(nameof({mappingContextParameterName}));")
            .WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));")
            .WriteLine()
            .WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);");

            // End constructor declaration
            return(builder.WriteClosingBracket());
        }
Exemplo n.º 3
0
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteLine("using System;")
                                .WriteLine()
                                .WriteLine($"namespace {RootNamespace}")
                                .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Specifies that the annotated class can be mapped from the provided <see cref=\"SourceType\"/>.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine("[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]")
            .WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
            .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine($"/// Initializes a new instance of the <see cref=\"{AttributeName}Attribute\"/> class with the specified <paramref name=\"sourceType\"/>.")
                .WriteLine("/// </summary>")
                .WriteLine("/// <param name=\"sourceType\">The type of to map from.</param>");
            }

            builder
            .WriteLine($"public {AttributeName}Attribute(Type sourceType)")
            .WriteOpeningBracket()
            .WriteLine("SourceType = sourceType;")
            .WriteClosingBracket()
            .WriteLine();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Gets the type to map from.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine("public Type SourceType { get; }")
            .WriteClosingBracket()     // class
            .WriteClosingBracket();    // namespace

            return(new(builder.ToString(), $"{AttributeName}Attribute.g.cs"));
        }
Exemplo n.º 4
0
        private static SourceBuilder GenerateSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model)
        {
            var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();

            return(builder
                   .GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName)
                   .WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]")
                   .WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.TypeIdentifierName}{model.Options.NullableReferenceSyntax} To{model.TypeIdentifierName}(this {model.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
                   .WriteOpeningBracket()
                   .WriteLine($"return {sourceClassParameterName} == null ? null : new {model.TypeIdentifierName}({sourceClassParameterName});")
                   .WriteClosingBracket());
        }
Exemplo n.º 5
0
        private static SourceBuilder GenerateConvertorMethodsXmlDocs(this SourceBuilder builder, MappingModel model, string sourceClassParameterName)
        {
            if (!model.Options.GenerateXmlDocument)
            {
                return(builder);
            }

            return(builder
                   .WriteLine("/// <summary>")
                   .WriteLine($"/// Creates a new instance of <see cref=\"{model.TypeIdentifierName}\"/> and sets its participating properties")
                   .WriteLine($"/// using the property values from <paramref name=\"{sourceClassParameterName}\"/>.")
                   .WriteLine("/// </summary>")
                   .WriteLine($"/// <param name=\"{sourceClassParameterName}\">The instance of <see cref=\"{model.SourceType}\"/> to use as source.</param>")
                   .WriteLine($"/// <returns>A new instance of <see cred=\"{model.TypeIdentifierName}\"/> -or- <c>null</c> if <paramref name=\"{sourceClassParameterName}\"/> is <c>null</c>.</returns>"));
        }
Exemplo n.º 6
0
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteNullableContextOptionIf(options.SupportNullableReferenceTypes)
                                .WriteLine()
                                .WriteLine("using System;")
                                .WriteLine()
                                .WriteLine($"namespace {RootNamespace}")
                                .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Specifies the mapping behavior of the annotated property.")
                .WriteLine("/// </summary>")
                .WriteLine("/// <remarks>")
                .WriteLine($"/// {AttributeClassName} has a number of uses:")
                .WriteLine("/// <list type=\"bullet\">")
                .WriteLine("/// <item><description>By default properties with same name will get mapped. This attribute allows the names to be different.</description></item>")
                .WriteLine("/// <item><description>Indicates that a property should be mapped when member serialization is set to opt-in.</description></item>")
                .WriteLine("/// </list>")
                .WriteLine("/// </remarks>");
            }

            builder
            .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]")
            .WriteLine($"public sealed class {AttributeClassName} : Attribute")
            .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Gets or sets the property name of the object to mapping from.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine($"public string{options.NullableReferenceSyntax} {SourcePropertyNamePropertyName} {{ get; set; }}")
            .WriteClosingBracket()     // class
            .WriteClosingBracket();    // namespace


            return(new(builder.ToString(), $"{AttributeClassName}.g.cs"));
        }
Exemplo n.º 7
0
        private static SourceBuilder GenerateSecondaryConstructor(this SourceBuilder builder, MappingModel model)
        {
            var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();

            if (model.Options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine($"/// Initializes a new instance of the <see cref=\"{model.TypeIdentifierName}\"/> class")
                .WriteLine($"/// using the property values from the specified <paramref name=\"{sourceClassParameterName}\"/>.")
                .WriteLine("/// </summary>")
                .WriteLine($"/// <exception cref=\"ArgumentNullException\">{sourceClassParameterName} is null</exception>");
            }

            return(builder
                   .WriteLine($"{model.Options.ConstructorAccessModifier.ToLowercaseString()} {model.TypeIdentifierName}({model.SourceType} {sourceClassParameterName})")
                   .WriteLine($"    : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}"));
        }
Exemplo n.º 8
0
        private static SourceBuilder GeneratePrivateConstructor(this SourceBuilder builder, MappingModel model)
        {
            var          sourceClassParameterName    = model.SourceTypeIdentifierName.ToCamelCase();
            const string mappingContextParameterName = "context";

            var baseConstructor = model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" : string.Empty;

            builder
            .WriteLine($"private protected {model.TypeIdentifierName}({MappingContextSource.ClassName} {mappingContextParameterName}, {model.SourceType} {sourceClassParameterName}){baseConstructor}")
            .WriteOpeningBracket()
            .WriteLine($"if ({mappingContextParameterName} == null) throw new ArgumentNullException(nameof({mappingContextParameterName}));")
            .WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));")
            .WriteLine()
            .WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);")
            .WriteLine();

            foreach (var property in model.MappedProperties)
            {
                if (property.TypeConverter is null)
                {
                    if (property.IsEnumerable)
                    {
                        builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName}.Select({mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.EnumerableTypeArgument}>).ToList();");
                    }
                    else
                    {
                        builder.WriteLine(property.MappedSourcePropertyTypeName is null
                            ? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
                            : $"{property.Name} = {mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.Type}>({sourceClassParameterName}.{property.SourcePropertyName});");
                    }
                }
                else
                {
                    var parameters = property.TypeConverterParameters.IsEmpty
                        ? "null"
                        : $"new object[] {{ {string.Join(", ", property.TypeConverterParameters)} }}";

                    builder.WriteLine($"{property.Name} = new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.SourcePropertyName}, {parameters});");
                }
            }

            // End constructor declaration
            return(builder.WriteClosingBracket());
        }
Exemplo n.º 9
0
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteNullableContextOptionIf(options.SupportNullableReferenceTypes)
                                .WriteLine()
                                .WriteLine($"namespace {RootNamespace}")
                                .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Converts the value of <typeparamref name=\"TSource\"/> to <typeparamref name=\"TDestination\"/>.")
                .WriteLine("/// </summary>")
                .WriteLine("/// <typeparam name=\"TSource\">The type to convert from.</typeparam>")
                .WriteLine("/// <typeparam name=\"TDestination\">The type to convert to.</typeparam>");
            }

            builder
            .WriteLine($"public interface {InterfaceName}<in TSource, out TDestination>")
            .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Converts the value of <paramref name=\"source\"/> object to <typeparamref name=\"TDestination\"/>.")
                .WriteLine("/// </summary>")
                .WriteLine("/// <param name=\"source\">The <see cref=\"TSource\"/> to convert.</param>")
                .WriteLine($"/// <param name=\"converterParameters\">The parameter list passed to the <see cref=\"{MapTypeConverterAttributeSource.AttributeClassName}\"/></param>")
                .WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>");
            }

            builder
            .WriteLine($"TDestination Convert(TSource source, object[]{options.NullableReferenceSyntax} converterParameters);")
            .WriteClosingBracket()
            .WriteClosingBracket();

            return(new(builder.ToString(), $"{InterfaceName}.g.cs"));
        }
Exemplo n.º 10
0
        internal static SourceCode Generate(MappingModel model)
        {
            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteNullableContextOptionIf(model.Options.SupportNullableReferenceTypes)
                                .WriteUsings(model.Usings)
                                .WriteLine()

                                // Namespace declaration
                                .WriteLine($"namespace {model.Namespace}")
                                .WriteOpeningBracket()

                                // Class declaration
                                .WriteLine($"partial record {model.TypeIdentifierName}")
                                .WriteOpeningBracket();

            // Class body
            if (model.GenerateSecondaryConstructor)
            {
                builder
                .GenerateSecondaryConstructor(model)
                .WriteLine();
            }

            builder
            .GeneratePrivateConstructor(model)
            .WriteLine()
            .GenerateFactoryMethod(model)

            // End class declaration
            .WriteClosingBracket()
            .WriteLine()

            // Extension class declaration
            .GenerateSourceTypeExtensionClass(model)

            // End namespace declaration
            .WriteClosingBracket();

            return(new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs"));
        }
Exemplo n.º 11
0
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            var builder = new SourceBuilder()
                          .WriteLine(GeneratedFilesHeader)
                          .WriteLine("using System;")
                          .WriteLine()
                          .WriteLine($"namespace {RootNamespace}")
                          .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Specifies that the annotated property should be excluded.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine("[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]")
            .WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}")
            .WriteClosingBracket();

            return(new(builder.ToString(), $"{AttributeClassName}.g.cs"));
        }
Exemplo n.º 12
0
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            var usings = new List <string> {
                "System", "System.Collections.Generic", "System.Reflection"
            };

            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteLine()
                                .WriteUsings(usings)
                                .WriteLine()

                                // Namespace declaration
                                .WriteLine($"namespace {RootNamespace}")
                                .WriteOpeningBracket()

                                // Class declaration
                                .WriteLine($"internal sealed class {ClassName}")
                                .WriteOpeningBracket()

                                .WriteLine("private readonly Dictionary<object, object> _cache;")
                                .WriteLine()

                                // Constructor
                                .WriteLine($"internal {ClassName}()")
                                .WriteOpeningBracket()
                                .WriteLine("_cache = new Dictionary<object, object>(1);")
                                .WriteClosingBracket()
                                .WriteLine()

                                // Factory
                                .WriteLine($"internal static TMapped {FactoryMethodName}<TOriginal, TMapped>(TOriginal original)")
                                .WriteOpeningBracket()
                                .WriteLine("if (original == null) throw new ArgumentNullException(nameof(original));")
                                .WriteLine()
                                .WriteLine("var context = new MappingContext();")
                                .WriteLine("var mapped = context.MapFromWithContext<TOriginal, TMapped>(original);")
                                .WriteLine()
                                .WriteLine("if (mapped == null)")
                                .WriteOpeningBracket()
                                .WriteLine("throw new InvalidOperationException();")
                                .WriteClosingBracket()
                                .WriteLine()
                                .WriteLine("return mapped;")
                                .WriteClosingBracket()
                                .WriteLine()

                                // MapFromWithContext method
                                .WriteLine($"internal TMapped MapFromWithContext<TOriginal, TMapped>(TOriginal original)")
                                .WriteOpeningBracket()
                                .WriteLine("if (original == null)")
                                .WriteOpeningBracket()
                                .WriteLine("return default(TMapped);")
                                .WriteClosingBracket()
                                .WriteLine()
                                .WriteLine("if (!TryGetValue<TOriginal, TMapped>(original, out var mapped))")
                                .WriteOpeningBracket()
                                .WriteLine("var instance = Activator.CreateInstance(typeof(TMapped), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { this, original }, null);")
                                .WriteLine("if (instance != null)")
                                .WriteOpeningBracket()
                                .WriteLine("mapped = (TMapped)instance;")
                                .WriteClosingBracket()
                                .WriteClosingBracket()
                                .WriteLine()
                                .WriteLine("return mapped;")
                                .WriteClosingBracket()
                                .WriteLine()

                                // Register method
                                .WriteLine("internal void Register<TOriginal, TMapped>(TOriginal original, TMapped mapped)")
                                .WriteOpeningBracket()
                                .WriteLine("if (original == null) throw new ArgumentNullException(nameof(original));")
                                .WriteLine("if (mapped == null) throw new ArgumentNullException(nameof(mapped));")
                                .WriteLine()
                                .WriteLine("if (!_cache.ContainsKey(original))")
                                .WriteOpeningBracket()
                                .WriteLine("_cache.Add(original, mapped);")
                                .WriteClosingBracket()
                                .WriteClosingBracket()
                                .WriteLine()

                                // TryGetValue method
                                .WriteLine("private bool TryGetValue<TOriginal, TMapped>(TOriginal original, out TMapped mapped)")
                                .WriteOpeningBracket()
                                .WriteLine("if (original != null && _cache.TryGetValue(original, out var value))")
                                .WriteOpeningBracket()
                                .WriteLine("mapped = (TMapped)value;")
                                .WriteLine("return true;")
                                .WriteClosingBracket()
                                .WriteLine()
                                .WriteLine("mapped = default(TMapped);")
                                .WriteLine("return false;")
                                .WriteClosingBracket()

                                // End class declaration
                                .WriteClosingBracket()

                                // End namespace declaration
                                .WriteClosingBracket();

            return(new(builder.ToString(), $"{ClassName}.g.cs"));
        }
        internal static SourceCode Generate(SourceGenerationOptions options)
        {
            using var builder = new SourceBuilder()
                                .WriteLine(GeneratedFilesHeader)
                                .WriteNullableContextOptionIf(options.SupportNullableReferenceTypes)
                                .WriteLine()
                                .WriteLine("using System;")
                                .WriteLine()
                                .WriteLine($"namespace {RootNamespace}")
                                .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine("/// Specifies what type to use as a converter for the property this attribute is bound to.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]")
            .WriteLine($"public sealed class {AttributeClassName} : Attribute")
            .WriteOpeningBracket();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine($"/// Initializes a new instance of <see cref=\"{AttributeClassName}\"/>.")
                .WriteLine("/// </summary>")
                .WriteLine($"/// <param name=\"converter\">The <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.</param>")
                .WriteLine("/// <param name=\"converterParameters\">The list of parameters to pass to the <paramref name=\"converter\"/> during the type conversion.</param>");
            }

            builder
            .WriteLine($"public {AttributeClassName}(Type converter, object[]{options.NullableReferenceSyntax} converterParameters = null)")
            .WriteOpeningBracket()
            .WriteLine($"{ConverterPropertyName} = converter;")
            .WriteLine($"{ConverterParametersPropertyName} = converterParameters;")
            .WriteClosingBracket()
            .WriteLine();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine($"/// Gets or sets the <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine($"public Type {ConverterPropertyName} {{ get; }}")
            .WriteLine();

            if (options.GenerateXmlDocument)
            {
                builder
                .WriteLine("/// <summary>")
                .WriteLine($"/// Gets the list of parameters to pass to the <see cref=\"{ConverterPropertyName}\"/> during the type conversion.")
                .WriteLine("/// </summary>");
            }

            builder
            .WriteLine($"public object[]{options.NullableReferenceSyntax} {ConverterParametersPropertyName} {{ get; }}")
            .WriteClosingBracket()
            .WriteClosingBracket();

            return(new(builder.ToString(), $"{AttributeClassName}.g.cs"));
        }