Exemple #1
0
        /// <inheritdoc />
        public void Execute(GeneratorExecutionContext context)
        {
            try
            {
                var options = SourceGenerationOptions.From(context);

                var compilation = context.Compilation
                                  .AddSource(ref context, MapFromAttributeSource.Generate(options))
                                  .AddSource(ref context, IgnorePropertyAttributeSource.Generate(options))
                                  .AddSource(ref context, ITypeConverterSource.Generate(options))
                                  .AddSource(ref context, MapTypeConverterAttributeSource.Generate(options))
                                  .AddSource(ref context, MapPropertyAttributeSource.Generate(options))
                                  .AddSource(ref context, MappingContextSource.Generate(options));

                if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateTypes.Any())
                {
                    AddGeneratedMappingsClasses(context, compilation, receiver.CandidateTypes, options);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }
        }
Exemple #2
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"));
        }
Exemple #3
0
        public static MappingContext Create(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
        {
            MappingContext context = typeSyntax switch
            {
                ClassDeclarationSyntax => new ClassMappingContext(compilation, sourceGenerationOptions, typeSyntax),
                RecordDeclarationSyntax => new RecordMappingContext(compilation, sourceGenerationOptions, typeSyntax),
                _ => throw new ArgumentOutOfRangeException()
            };

            context.Model = context.CreateMappingModel();

            return(context);
        }
        public override string[] GenerateSource(SourceGenerationOptions options)
        {
            var generatedClassName = options.Type.Name + "_" + options.FactoryOptions.ClassName;

            var text = this.CSharpTemplate()
                       .Replace("<codeGenerationTool>", $"{nameof(ConsoleTelemeter)} {Assembly.GetExecutingAssembly().GetName().Version} {DateTimeOffset.UtcNow:yyyy-MM-dd HH:mm:ss zzz}")
                       .Replace("<generatedNamespace>", options.FactoryOptions.Namespace)
                       .Replace("<generatedClassName>", generatedClassName)
                       .Replace("<interfaceImplemented>", options.Type.FullName)
                       .Replace("//<privateFields>", "private const string Null = \"<null>\";")
                       .Replace("//<publicMethodImplementations>", Methods(options.Methods))
                       .Replace("\t", "    ")
            ;

            return(new[] { text, $"{options.FactoryOptions.Namespace}.{generatedClassName}" });
        }
Exemple #5
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"));
        }
Exemple #6
0
        protected MappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
        {
            _ignoredNamespaces      = new();
            Diagnostics             = ImmutableArray <Diagnostic> .Empty;
            Usings                  = ImmutableArray.Create("System", Constants.RootNamespace);
            SourceGenerationOptions = sourceGenerationOptions;
            TypeSyntax              = typeSyntax;
            Compilation             = compilation;

            IgnorePropertyAttributeTypeSymbol   = compilation.GetTypeByMetadataNameOrThrow(IgnorePropertyAttributeSource.FullyQualifiedName);
            MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapTypeConverterAttributeSource.FullyQualifiedName);
            TypeConverterInterfaceTypeSymbol    = compilation.GetTypeByMetadataNameOrThrow(ITypeConverterSource.FullyQualifiedName);
            MapPropertyAttributeTypeSymbol      = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName);
            MapFromAttributeTypeSymbol          = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName);
            MappingContextTypeSymbol            = compilation.GetTypeByMetadataNameOrThrow(MappingContextSource.FullyQualifiedName);

            AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
        }
Exemple #7
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"));
        }
Exemple #8
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"));
        }
        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"));
        }
Exemple #10
0
 internal ClassMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
     : base(compilation, sourceGenerationOptions, typeSyntax)
 {
 }
        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"));
        }
Exemple #12
0
        private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, Compilation compilation, IEnumerable <TypeDeclarationSyntax> candidateTypes, SourceGenerationOptions options)
        {
            foreach (var typeDeclarationSyntax in candidateTypes)
            {
                var mappingContext = MappingContext.Create(compilation, options, typeDeclarationSyntax);
                mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);

                if (mappingContext.Model is null)
                {
                    continue;
                }

                var(source, hintName) = typeDeclarationSyntax switch
                {
                    ClassDeclarationSyntax => MapClassSource.Generate(mappingContext.Model),
                    RecordDeclarationSyntax => MapRecordSource.Generate(mappingContext.Model),
                    _ => throw new ArgumentOutOfRangeException()
                };

                context.AddSource(hintName, source);
            }
        }
    }