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 ExpressionSyntax MapExpression(ExpressionSyntax sourceExpression, ITypeSymbol sourceType, ITypeSymbol destinationType) { var mappingSource = new MappingElement { Expression = sourceExpression, ExpressionType = sourceType }; return(MapExpression(mappingSource, destinationType).Expression); }
private ExpressionSyntax HandleSafeNull(MappingElement source, AnnotatedType targetType, ExpressionSyntax expression) { if (source.ExpressionType.CanBeNull) { var condition = BinaryExpression(SyntaxKind.NotEqualsExpression, source.Expression, LiteralExpression(SyntaxKind.NullLiteralExpression)); var whenNull = targetType.CanBeNull ? (ExpressionSyntax)LiteralExpression(SyntaxKind.NullLiteralExpression) : ThrowNullArgumentException(source.Expression.ToFullString()); return(ConditionalExpression(condition, expression, whenNull)); } return(expression); }
public ExpressionSyntax MapExpression(ExpressionSyntax sourceExpression, AnnotatedType sourceType, AnnotatedType destinationType, MappingContext mappingContext) { var mappingSource = new MappingElement { Expression = sourceExpression, ExpressionType = sourceType }; return(MapExpression(mappingSource, destinationType, mappingContext).Expression); }
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 <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)); }
public async Task <ExpressionSyntax> MapExpression(ExpressionSyntax sourceExpression, AnnotatedType sourceType, AnnotatedType destinationType, MappingContext mappingContext) { var mappingSource = new MappingElement { Expression = sourceExpression, ExpressionType = sourceType }; var mappingElement = await MapExpression(mappingSource, destinationType, mappingContext).ConfigureAwait(false); return(mappingElement.Expression); }
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)); }
protected override MappingElement TryToCreateMappingExpression(MappingElement source, ITypeSymbol targetType, MappingPath mappingPath) { //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"); if (cloneMethods.Any(IsGenericCloneMethod)) { return(new MappingElement() { ExpressionType = targetType, Expression = invokeClone as ExpressionSyntax }); } var objectClone = cloneMethods.FirstOrDefault(x => x is IMethodSymbol md && md.Parameters.Length == 0); if (objectClone != null) { var objectCLoneMethod = (IMethodSymbol)objectClone; if (CanBeAccessedInCurrentContext(objectCLoneMethod)) { 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)); }
protected override MappingElement TryToCreateMappingExpression(MappingElement source, AnnotatedType targetType, MappingPath mappingPath, MappingContext mappingContext) { //TODO: check if source is not null (conditional member access) if (mappingPath.Length > 1 && source.ExpressionType.Type.AllInterfaces.Any(x => x.Name == "ICloneable") && source.ExpressionType.Type.SpecialType != SpecialType.System_Array) { var invokeClone = syntaxGenerator.InvocationExpression(syntaxGenerator.MemberAccessExpression(source.Expression, "Clone")); var cloneMethods = targetType.Type.GetMembers("Clone").OfType <IMethodSymbol>().Where(m => mappingContext.AccessibilityHelper.IsSymbolAccessible(m, targetType.Type)).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.Type) as ExpressionSyntax }); } var implicitClone = targetType.Type.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.Type) as ExpressionSyntax }); } } return(base.TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext)); }
private MappingElement TryToUnwrap(ITypeSymbol targetType, MappingElement element) { var sourceAccess = element.Expression as SyntaxNode; var conversion = semanticModel.Compilation.ClassifyConversion(element.ExpressionType, targetType); if (conversion.Exists == false) { var wrapper = GetWrappingInfo(element.ExpressionType, targetType); if (wrapper.Type == WrapperInfoType.Property) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.MemberAccessExpression(sourceAccess, wrapper.UnwrappingProperty.Name), ExpressionType = wrapper.UnwrappingProperty.Type }); } if (wrapper.Type == WrapperInfoType.Method) { var unwrappingMethodAccess = syntaxGenerator.MemberAccessExpression(sourceAccess, wrapper.UnwrappingMethod.Name); return(new MappingElement() { Expression = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(unwrappingMethodAccess), ExpressionType = wrapper.UnwrappingMethod.ReturnType }); } if (targetType.SpecialType == SpecialType.System_String && element.ExpressionType.TypeKind == TypeKind.Enum) { var toStringAccess = syntaxGenerator.MemberAccessExpression(element.Expression, "ToString"); return(new MappingElement() { Expression = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(toStringAccess), ExpressionType = targetType }); } if (element.ExpressionType.SpecialType == SpecialType.System_String && targetType.TypeKind == TypeKind.Enum) { var parseEnumAccess = syntaxGenerator.MemberAccessExpression(SyntaxFactory.ParseTypeName("System.Enum"), "Parse"); var enumType = SyntaxFactory.ParseTypeName(targetType.Name); var parseInvocation = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(parseEnumAccess, new[] { syntaxGenerator.TypeOfExpression(enumType), element.Expression, syntaxGenerator.TrueLiteralExpression() }); return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(enumType, parseInvocation), ExpressionType = targetType }); } } else if (conversion.IsExplicit) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(targetType, sourceAccess), ExpressionType = targetType }); } return(element); }
private bool IsUnwrappingNeeded(ITypeSymbol targetType, MappingElement element) { return(targetType.Equals(element.ExpressionType) == false && (ObjectHelper.IsSimpleType(targetType) || SymbolHelper.IsNullable(targetType, out _))); }
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); }
private MappingElement ConvertToSimpleType(AnnotatedType targetType, MappingElement source, MappingContext mappingContext) { var conversion = semanticModel.Compilation.ClassifyConversion(source.ExpressionType.Type, targetType.Type); if (conversion.Exists == false) { var wrapper = GetWrappingInfo(source.ExpressionType.Type, targetType.Type, mappingContext); if (wrapper.Type == WrapperInfoType.ObjectField) { return(new MappingElement { Expression = SyntaxFactoryExtensions.CreateMemberAccessExpression(source.Expression, source.ExpressionType.CanBeNull, wrapper.UnwrappingObjectField.Name), ExpressionType = wrapper.UnwrappingObjectField.Type }); } if (wrapper.Type == WrapperInfoType.Method) { return(new MappingElement { Expression = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, wrapper.UnwrappingMethod.Name), ExpressionType = new AnnotatedType(wrapper.UnwrappingMethod.ReturnType) }); } if (targetType.Type.SpecialType == SpecialType.System_String && source.ExpressionType.Type.TypeKind == TypeKind.Enum) { var toStringAccess = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, "ToString"); return(new MappingElement { Expression = toStringAccess, ExpressionType = targetType }); } if (source.ExpressionType.Type.SpecialType == SpecialType.System_String && targetType.Type.TypeKind == TypeKind.Enum) { var parseEnumAccess = syntaxGenerator.MemberAccessExpression(SyntaxFactory.ParseTypeName("System.Enum"), "Parse"); var enumType = SyntaxFactory.ParseTypeName(targetType.Type.Name); var parseInvocation = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(parseEnumAccess, new[] { syntaxGenerator.TypeOfExpression(enumType), source.Expression, syntaxGenerator.TrueLiteralExpression() }); return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(enumType, parseInvocation), ExpressionType = targetType.AsNotNull() }); } } else if (conversion.IsExplicit) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(targetType.Type, source.Expression), ExpressionType = targetType }); } return(source); }
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) }); }