コード例 #1
0
        public MappingElement MapExpression(MappingElement source, ITypeSymbol targetType, MappingContext mappingContext, MappingPath mappingPath = null)
        {
            if (source == null)
            {
                return(null);
            }

            if (mappingPath == null)
            {
                mappingPath = new MappingPath();
            }

            var sourceType = source.ExpressionType;

            if (mappingPath.AddToMapped(sourceType) == false)
            {
                return(new MappingElement()
                {
                    ExpressionType = sourceType,
                    Expression = source.Expression.WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */"))
                });
            }


            if (mappingContext.FindConversion(sourceType, targetType) is {} userDefinedConversion)
            {
                return(new MappingElement()
                {
                    ExpressionType = targetType,
                    Expression = (ExpressionSyntax)syntaxGenerator.InvocationExpression(userDefinedConversion, source.Expression)
                });
            }


            if (ObjectHelper.IsSimpleType(targetType) && SymbolHelper.IsNullable(sourceType, out var underlyingType))
            {
                source = new MappingElement()
                {
                    Expression     = (ExpressionSyntax)syntaxGenerator.MemberAccessExpression(source.Expression, "Value"),
                    ExpressionType = underlyingType
                };
            }

            if (IsUnwrappingNeeded(targetType, source))
            {
                return(TryToUnwrap(targetType, source, mappingContext));
            }

            if (ShouldCreateConversionBetweenTypes(targetType, sourceType))
            {
                return(TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext));
            }

            return(source);
        }
コード例 #2
0
        public async Task <ObjectCreationExpressionSyntax> AddInitializerWithMappingAsync(
            ObjectCreationExpressionSyntax objectCreationExpression, IMappingMatcher mappingMatcher,
            ITypeSymbol createdObjectTyp,
            MappingContext mappingContext,
            MappingPath mappingPath = null)
        {
            var propertiesToSet = _mappingTargetHelper.GetFieldsThaCanBeSetPublicly(createdObjectTyp, mappingContext);
            var assignments     = await MapUsingSimpleAssignment(propertiesToSet, mappingMatcher, mappingContext, mappingPath).ConfigureAwait(false);

            return(SyntaxFactoryExtensions.WithMembersInitialization(objectCreationExpression, assignments));
        }
コード例 #3
0
        public ObjectCreationExpressionSyntax AddInitializerWithMapping(
            ObjectCreationExpressionSyntax objectCreationExpression, IMappingMatcher mappingMatcher,
            ITypeSymbol createdObjectTyp,
            MappingContext mappingContext,
            MappingPath mappingPath = null)
        {
            var propertiesToSet = MappingTargetHelper.GetFieldsThaCanBeSetPublicly(createdObjectTyp, mappingContext);
            var assignments     = MapUsingSimpleAssignment(propertiesToSet, mappingMatcher, mappingContext, mappingPath).ToList();

            return(SyntaxFactoryExtensions.WithMembersInitialization(objectCreationExpression, assignments));
        }
コード例 #4
0
        public SyntaxNode CreateMappingLambda(string lambdaParameterName, ITypeSymbol sourceListElementType,
                                              ITypeSymbol targetListElementType, MappingPath mappingPath, MappingContext mappingContext)
        {
            var source = new MappingElement()
            {
                ExpressionType = sourceListElementType,
                Expression     = syntaxGenerator.IdentifierName(lambdaParameterName) as ExpressionSyntax
            };
            var listElementMappingStm = MapExpression(source, targetListElementType, mappingContext, mappingPath);

            return(syntaxGenerator.ValueReturningLambdaExpression(lambdaParameterName, listElementMappingStm.Expression));
        }
コード例 #5
0
        public IEnumerable <ExpressionSyntax> MapUsingSimpleAssignment(IEnumerable <IPropertySymbol> targets, IMappingMatcher mappingMatcher,
                                                                       MappingPath mappingPath = null, SyntaxNode globalTargetAccessor = null)
        {
            if (mappingPath == null)
            {
                mappingPath = new MappingPath();
            }

            return(mappingMatcher.MatchAll(targets, syntaxGenerator, globalTargetAccessor)
                   .Select(match =>
            {
                var sourceExpression = this.MapExpression(match.Source, match.Target.ExpressionType, mappingPath.Clone()).Expression;
                return (ExpressionSyntax)syntaxGenerator.AssignmentStatement(match.Target.Expression, sourceExpression);
            }).ToList());
        }
コード例 #6
0
        public ObjectCreationExpressionSyntax AddInitializerWithMapping(
            ObjectCreationExpressionSyntax objectCreationExpression, IMappingMatcher mappingMatcher,
            ITypeSymbol createdObjectTyp,
            MappingPath mappingPath = null)
        {
            var propertiesToSet = ObjectHelper.GetFieldsThaCanBeSetPublicly(createdObjectTyp, contextAssembly);
            var assignments     = MapUsingSimpleAssignment(propertiesToSet, mappingMatcher, mappingPath).ToList();

            if (assignments.Count == 0)
            {
                return(objectCreationExpression);
            }
            var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, new SeparatedSyntaxList <ExpressionSyntax>().AddRange(assignments)).FixInitializerExpressionFormatting(objectCreationExpression);

            return(objectCreationExpression.WithInitializer(initializerExpressionSyntax));
        }
コード例 #7
0
        protected override MappingElement TryToCreateMappingExpression(MappingElement source, ITypeSymbol targetType,
                                                                       MappingPath mappingPath, MappingContext mappingContext)
        {
            //TODO: check if source is not null (conditional member access)

            if (mappingPath.Length > 1 && source.ExpressionType.AllInterfaces.Any(x => x.Name == "ICloneable") && source.ExpressionType.SpecialType != SpecialType.System_Array)
            {
                var invokeClone  = syntaxGenerator.InvocationExpression(syntaxGenerator.MemberAccessExpression(source.Expression, "Clone"));
                var cloneMethods = targetType.GetMembers("Clone").OfType <IMethodSymbol>().Where(m => mappingContext.AccessibilityHelper.IsSymbolAccessible(m, targetType)).ToList();
                if (cloneMethods.Any(IsGenericCloneMethod))
                {
                    return(new MappingElement()
                    {
                        ExpressionType = targetType,
                        Expression = invokeClone as ExpressionSyntax
                    });
                }

                var objectClone = cloneMethods.FirstOrDefault(x => x.Parameters.Length == 0);

                if (objectClone != null)
                {
                    return(new MappingElement()
                    {
                        ExpressionType = targetType,
                        Expression = syntaxGenerator.TryCastExpression(invokeClone, targetType) as ExpressionSyntax
                    });
                }

                var implicitClone = targetType.GetMembers("System.ICloneable.Clone").FirstOrDefault();
                if (implicitClone != null)
                {
                    var castedOnICloneable = syntaxGenerator.CastExpression(SyntaxFactory.ParseTypeName("ICloneable"), source.Expression);
                    return(new MappingElement()
                    {
                        ExpressionType = targetType,
                        Expression = syntaxGenerator.TryCastExpression(syntaxGenerator.InvocationExpression(syntaxGenerator.MemberAccessExpression(castedOnICloneable, "Clone")), targetType) as ExpressionSyntax
                    });
                }
            }

            return(base.TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext));
        }
コード例 #8
0
        private SyntaxNode MapCollections(SyntaxNode sourceAccess, ITypeSymbol sourceListType,
                                          ITypeSymbol targetListType, MappingPath mappingPath, MappingContext mappingContext)
        {
            var isReadonlyCollection  = ObjectHelper.IsReadonlyCollection(targetListType);
            var sourceListElementType = MappingHelper.GetElementType(sourceListType);
            var targetListElementType = MappingHelper.GetElementType(targetListType);

            if (ShouldCreateConversionBetweenTypes(targetListElementType, sourceListElementType))
            {
                var useConvert          = CanUseConvert(sourceListType);
                var selectAccess        = useConvert ?   syntaxGenerator.MemberAccessExpression(sourceAccess, "ConvertAll"): syntaxGenerator.MemberAccessExpression(sourceAccess, "Select");
                var lambdaParameterName = NameHelper.CreateLambdaParameterName(sourceAccess);
                var mappingLambda       = CreateMappingLambda(lambdaParameterName, sourceListElementType, targetListElementType, mappingPath, mappingContext);
                var selectInvocation    = syntaxGenerator.InvocationExpression(selectAccess, mappingLambda);
                var toList = useConvert? selectInvocation: AddMaterializeCollectionInvocation(syntaxGenerator, selectInvocation, targetListType);
                return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator));
            }

            var toListInvocation = AddMaterializeCollectionInvocation(syntaxGenerator, sourceAccess, targetListType);

            return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toListInvocation, isReadonlyCollection, syntaxGenerator));
        }
コード例 #9
0
        protected virtual MappingElement TryToCreateMappingExpression(MappingElement source, ITypeSymbol targetType, MappingPath mappingPath)
        {
            //TODO: If source expression is method or constructor invocation then we should extract local variable and use it im mappings as a reference
            var namedTargetType = targetType as INamedTypeSymbol;

            if (namedTargetType != null)
            {
                var directlyMappingConstructor = namedTargetType.Constructors.FirstOrDefault(c => c.Parameters.Length == 1 && c.Parameters[0].Type.Equals(source.ExpressionType));
                if (directlyMappingConstructor != null)
                {
                    var constructorParameters = SyntaxFactory.ArgumentList().AddArguments(SyntaxFactory.Argument(source.Expression));
                    var creationExpression    = syntaxGenerator.ObjectCreationExpression(targetType, constructorParameters.Arguments);
                    return(new MappingElement()
                    {
                        ExpressionType = targetType,
                        Expression = (ExpressionSyntax)creationExpression
                    });
                }
            }

            if (MappingHelper.IsMappingBetweenCollections(targetType, source.ExpressionType))
            {
                return(new MappingElement()
                {
                    ExpressionType = targetType,
                    Expression = MapCollections(source.Expression, source.ExpressionType, targetType, mappingPath.Clone()) as ExpressionSyntax
                });
            }

            var subMappingSourceFinder = new ObjectMembersMappingSourceFinder(source.ExpressionType, source.Expression, syntaxGenerator);

            if (namedTargetType != null)
            {
                //maybe there is constructor that accepts parameter matching source properties
                var constructorOverloadParameterSets = namedTargetType.Constructors.Select(x => x.Parameters);
                var matchedOverload = MethodHelper.FindBestParametersMatch(subMappingSourceFinder, constructorOverloadParameterSets);

                if (matchedOverload != null)
                {
                    var creationExpression = ((ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(targetType, matchedOverload.ToArgumentListSyntax(this).Arguments));
                    var matchedSources     = matchedOverload.GetMatchedSources();
                    var restSourceFinder   = new IgnorableMappingSourceFinder(subMappingSourceFinder, foundElement =>
                    {
                        return(matchedSources.Any(x => x.Expression.IsEquivalentTo(foundElement.Expression)));
                    });
                    var mappingMatcher = new SingleSourceMatcher(restSourceFinder);
                    return(new MappingElement()
                    {
                        ExpressionType = targetType,
                        Expression = AddInitializerWithMapping(creationExpression, mappingMatcher, targetType, mappingPath)
                    });
                }
            }


            var objectCreationExpressionSyntax = ((ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(targetType));
            var subMappingMatcher = new SingleSourceMatcher(subMappingSourceFinder);

            return(new MappingElement()
            {
                ExpressionType = targetType,
                Expression = AddInitializerWithMapping(objectCreationExpressionSyntax, subMappingMatcher, targetType, mappingPath)
            });
        }
コード例 #10
0
        public async Task <MappingElement> MapExpression(MappingElement source, AnnotatedType targetType, MappingContext mappingContext, MappingPath mappingPath = null)
        {
            if (source == null)
            {
                return(null);
            }

            if (mappingPath == null)
            {
                mappingPath = new MappingPath();
            }

            var sourceType = source.ExpressionType;

            if (mappingPath.AddToMapped(sourceType.Type) == false)
            {
                return(new MappingElement()
                {
                    ExpressionType = sourceType,
                    Expression = source.Expression.WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */")),
                });
            }

            if (mappingContext.FindConversion(sourceType, targetType) is {} userDefinedConversion)
            {
                //TODO: Check if conversion accept nullable type
                var invocationExpression  = (ExpressionSyntax)syntaxGenerator.InvocationExpression(userDefinedConversion.Conversion, source.Expression);
                var protectResultFromNull = targetType.CanBeNull == false && userDefinedConversion.ToType.CanBeNull;
                var conversionExpression  = protectResultFromNull ? OrFailWhenExpressionNull(invocationExpression) : invocationExpression;
                return(new MappingElement
                {
                    ExpressionType = targetType,
                    Expression = HandleSafeNull(source, userDefinedConversion.FromType, conversionExpression)
                });
            }


            if (ObjectHelper.IsSimpleType(targetType.Type) && SymbolHelper.IsNullable(sourceType.Type, out var underlyingType))
            {
                return(new MappingElement
                {
                    Expression = OrFailWhenArgumentNull(source.Expression),
                    ExpressionType = new AnnotatedType(underlyingType, false)
                });
            }

            if (IsConversionToSimpleTypeNeeded(targetType.Type, source.ExpressionType.Type))
            {
                var conversion = ConvertToSimpleType(targetType, source, mappingContext);
                if (targetType.CanBeNull == false && conversion.ExpressionType.CanBeNull)
                {
                    return(new MappingElement
                    {
                        ExpressionType = conversion.ExpressionType.AsNotNull(),
                        Expression = OrFailWhenArgumentNull(conversion.Expression)
                    });
                }
                return(conversion);
            }

            if (ShouldCreateConversionBetweenTypes(targetType.Type, sourceType.Type))
            {
                return(await  TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext).ConfigureAwait(false));
            }


            if (source.ExpressionType.Type.Equals(targetType.Type) && source.ExpressionType.CanBeNull && targetType.CanBeNull == false)
            {
                return(new MappingElement
                {
                    Expression = OrFailWhenArgumentNull(source.Expression),
                    ExpressionType = source.ExpressionType.AsNotNull()
                });
            }
            return(source);
        }
コード例 #11
0
        public async Task <ExpressionSyntax> CreateMappingLambdaAsync(string lambdaParameterName, AnnotatedType sourceListElementType, AnnotatedType targetListElementType, MappingPath mappingPath, MappingContext mappingContext)
        {
            var source = new MappingElement()
            {
                ExpressionType = sourceListElementType,
                Expression     = syntaxGenerator.IdentifierName(lambdaParameterName) as ExpressionSyntax,
            };
            var listElementMappingStm = await MapExpression(source, targetListElementType, mappingContext, mappingPath).ConfigureAwait(false);

            return((ExpressionSyntax)syntaxGenerator.ValueReturningLambdaExpression(lambdaParameterName, listElementMappingStm.Expression));
        }
コード例 #12
0
        private async Task <SyntaxNode> MapCollectionsAsync(MappingElement source, AnnotatedType targetListType, MappingPath mappingPath, MappingContext mappingContext)
        {
            var isReadonlyCollection  = ObjectHelper.IsReadonlyCollection(targetListType.Type);
            var sourceListElementType = MappingHelper.GetElementType(source.ExpressionType.Type);
            var targetListElementType = MappingHelper.GetElementType(targetListType.Type);

            if (sourceListElementType.CanBeNull && targetListElementType.CanBeNull == false)
            {
                var whereFilter         = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, $"OfType<{sourceListElementType.Type.Name}>");
                var lambdaParameterName = NameHelper.CreateLambdaParameterName(source.Expression);
                var mappingLambda       = await CreateMappingLambdaAsync(lambdaParameterName, sourceListElementType.AsNotNull(), targetListElementType, mappingPath, mappingContext).ConfigureAwait(false);

                var selectExpression = SyntaxFactoryExtensions.CreateMethodAccessExpression(whereFilter, false, "Select", mappingLambda);
                var toList           = AddMaterializeCollectionInvocation(syntaxGenerator, selectExpression, targetListType.Type, false);
                return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator));
            }

            if (ShouldCreateConversionBetweenTypes(targetListElementType.Type, sourceListElementType.Type))
            {
                var useConvert          = CanUseConvert(source.ExpressionType.Type);
                var mapMethod           = useConvert ? "ConvertAll": "Select";
                var lambdaParameterName = NameHelper.CreateLambdaParameterName(source.Expression);
                var mappingLambda       = await CreateMappingLambdaAsync(lambdaParameterName, sourceListElementType, targetListElementType, mappingPath, mappingContext).ConfigureAwait(false);

                var selectExpression = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, mapMethod, mappingLambda);
                var toList           = useConvert? selectExpression: AddMaterializeCollectionInvocation(syntaxGenerator, selectExpression, targetListType.Type, false);
                return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator));
            }

            var toListInvocation = AddMaterializeCollectionInvocation(syntaxGenerator, source.Expression, targetListType.Type, source.ExpressionType.CanBeNull);

            return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toListInvocation, isReadonlyCollection, syntaxGenerator));
        }
コード例 #13
0
        public async Task <IReadOnlyList <AssignmentExpressionSyntax> > MapUsingSimpleAssignment(IReadOnlyCollection <IObjectField> targets,
                                                                                                 IMappingMatcher mappingMatcher,
                                                                                                 MappingContext mappingContext,
                                                                                                 MappingPath mappingPath = null, SyntaxNode globalTargetAccessor = null)
        {
            if (mappingPath == null)
            {
                mappingPath = new MappingPath();
            }

            var results = new List <AssignmentExpressionSyntax>();

            foreach (var match in await mappingMatcher.MatchAll(targets, syntaxGenerator, mappingContext, globalTargetAccessor).ConfigureAwait(false))
            {
                var sourceMappingElement = await MapExpression(match.Source, match.Target.ExpressionType, mappingContext, mappingPath.Clone()).ConfigureAwait(false);

                var sourceExpression = sourceMappingElement.Expression;
                if (sourceMappingElement.ExpressionType != match.Target.ExpressionType)
                {
                    mappingContext.AddMissingConversion(sourceMappingElement.ExpressionType.Type, match.Target.ExpressionType.Type);
                    if (mappingContext.WrapInCustomConversion)
                    {
                        var customConversionMethodName = syntaxGenerator.IdentifierName($"MapFrom{sourceMappingElement.ExpressionType.Type.Name}To{match.Target.ExpressionType.Type.Name}");
                        sourceExpression = (ExpressionSyntax)syntaxGenerator.InvocationExpression(customConversionMethodName, sourceExpression);
                    }
                }

                var assignmentExpression = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, match.Target.Expression, sourceExpression);
                results.Add(assignmentExpression);
            }

            return(results);
        }
コード例 #14
0
        protected virtual async Task <MappingElement> TryToCreateMappingExpression(MappingElement source, AnnotatedType targetType, MappingPath mappingPath, MappingContext mappingContext)
        {
            //TODO: If source expression is method or constructor invocation then we should extract local variable and use it im mappings as a reference
            var namedTargetType = targetType.Type as INamedTypeSymbol;

            if (namedTargetType != null)
            {
                var directlyMappingConstructor = namedTargetType.Constructors.FirstOrDefault(c => c.Parameters.Length == 1 && c.Parameters[0].Type.Equals(source.ExpressionType.Type));
                if (directlyMappingConstructor != null)
                {
                    var creationExpression       = CreateObject(targetType.Type, SyntaxFactory.ArgumentList().AddArguments(SyntaxFactory.Argument(source.Expression)));
                    var shouldProtectAgainstNull = directlyMappingConstructor.Parameters[0].Type.CanBeNull() == false && source.ExpressionType.CanBeNull;
                    return(new MappingElement
                    {
                        ExpressionType = targetType,
                        Expression = shouldProtectAgainstNull ? HandleSafeNull(source, targetType, creationExpression) : creationExpression
                    });
                }
            }

            if (MappingHelper.IsMappingBetweenCollections(targetType.Type, source.ExpressionType.Type))
            {
                var shouldProtectAgainstNull = source.ExpressionType.CanBeNull && targetType.CanBeNull == false;
                var collectionMapping        = (await MapCollectionsAsync(source, targetType, mappingPath.Clone(), mappingContext).ConfigureAwait(false)) as ExpressionSyntax;
                return(new MappingElement
                {
                    ExpressionType = targetType,
                    Expression = shouldProtectAgainstNull ? OrFailWhenArgumentNull(collectionMapping, source.Expression.ToFullString()) : collectionMapping,
                });
            }

            var subMappingSourceFinder = new ObjectMembersMappingSourceFinder(source.ExpressionType.AsNotNull(), source.Expression);

            if (namedTargetType != null)
            {
                //maybe there is constructor that accepts parameter matching source properties
                var constructorOverloadParameterSets = namedTargetType.Constructors.Select(x => x.Parameters);
                var matchedOverload = await MethodHelper.FindBestParametersMatch(subMappingSourceFinder, constructorOverloadParameterSets, mappingContext).ConfigureAwait(false);

                if (matchedOverload != null)
                {
                    var argumentListSyntaxAsync = await matchedOverload.ToArgumentListSyntaxAsync(this, mappingContext).ConfigureAwait(false);

                    var creationExpression = CreateObject(targetType.Type, argumentListSyntaxAsync);
                    var matchedSources     = matchedOverload.GetMatchedSources();
                    var restSourceFinder   = new IgnorableMappingSourceFinder(subMappingSourceFinder, foundElement =>
                    {
                        return(matchedSources.Any(x => x.Expression.IsEquivalentTo(foundElement.Expression)));
                    });
                    var mappingMatcher = new SingleSourceMatcher(restSourceFinder);
                    return(new MappingElement()
                    {
                        ExpressionType = new AnnotatedType(targetType.Type),
                        Expression = await AddInitializerWithMappingAsync(creationExpression, mappingMatcher, targetType.Type, mappingContext, mappingPath).ConfigureAwait(false),
                    });
                }
            }

            var objectCreationExpressionSyntax = CreateObject(targetType.Type);
            var subMappingMatcher             = new SingleSourceMatcher(subMappingSourceFinder);
            var objectCreationWithInitializer = await AddInitializerWithMappingAsync(objectCreationExpressionSyntax, subMappingMatcher, targetType.Type, mappingContext, mappingPath).ConfigureAwait(false);

            return(new MappingElement()
            {
                ExpressionType = new AnnotatedType(targetType.Type),
                Expression = HandleSafeNull(source, targetType, objectCreationWithInitializer)
            });
        }