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); }
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)); }
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)); }
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)); }
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()); }
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)); }
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)); }
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)); }
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) }); }
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); }
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)); }
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)); }
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); }
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) }); }