protected override bool ShouldCreateConversionBetweenTypes(ITypeSymbol targetType, ITypeSymbol sourceType) { if (targetType.Equals(sourceType) && SymbolHelper.IsNullable(targetType, out _)) { return(false); } return(ObjectHelper.IsSimpleType(targetType) == false && ObjectHelper.IsSimpleType(sourceType) == false); }
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); }
private SyntaxNode GetDefaultExpression(ITypeSymbol type, MappingContext mappingContext, MappingPath mappingPath) { if (mappingPath.AddToMapped(type) == false) { return(syntaxGenerator.DefaultExpression(type) .WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */"))); } return(cache.GetOrAdd(type.ToDisplayString().TrimEnd('?'), _ => { if (SymbolHelper.IsNullable(type, out var underlyingType)) { type = underlyingType; } if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol) { var enumOption = namedTypeSymbol.MemberNames.Where(x => x != "value__" && x != ".ctor").OrderBy(x => x).FirstOrDefault(); if (enumOption != null) { return SyntaxFactoryExtensions.CreateMemberAccessExpression(SyntaxFactory.IdentifierName(namedTypeSymbol.Name), false, enumOption); } return syntaxGenerator.DefaultExpression(type); } if (type.SpecialType == SpecialType.None) { ObjectCreationExpressionSyntax objectCreationExpression = null; if (MappingHelper.IsCollection(type)) { var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type); if (type is IArrayTypeSymbol) { objectCreationExpression = CreateObject(type); } else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection) { if (type is INamedTypeSymbol namedType && namedType.IsGenericType) { var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x => syntaxGenerator.TypeExpression(x)))); var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax); objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); }
private MappingElement FindSubPropertySource(string targetName, ITypeSymbol containingType, IEnumerable <IObjectField> properties, SyntaxNode currentAccessor, MappingContext mappingContext, bool isCurrentAccessorNullable, string prefix = null) { if (ObjectHelper.IsSimpleType(containingType)) { return(null); } var subProperty = properties.Where(x => SanitizeName(targetName).StartsWith(SanitizeName($"{prefix}{x.Name}"), StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(p => p.CanBeGet(containingType, mappingContext)); if (subProperty != null) { var currentNamePart = $"{prefix}{subProperty.Name}"; var subPropertyAccessor = SyntaxFactoryExtensions.CreateMemberAccessExpression((ExpressionSyntax)currentAccessor, isCurrentAccessorNullable, subProperty.Name); var expressionCanBeNull = isCurrentAccessorNullable || subProperty.Type.CanBeNull; if (SanitizeName(targetName).Equals(SanitizeName(currentNamePart), StringComparison.OrdinalIgnoreCase)) { //Special Case: x.YValue = z.Y.Value if (subProperty.Name == "Value" && SymbolHelper.IsNullable(containingType, out var _)) { return(new MappingElement { Expression = (ExpressionSyntax)currentAccessor, ExpressionType = new AnnotatedType(containingType, true) }); } return(new MappingElement { Expression = subPropertyAccessor, ExpressionType = new AnnotatedType(subProperty.Type.Type, expressionCanBeNull) }); } return(FindSubPropertySource(targetName, subProperty.Type.Type, GetFields(subProperty.Type.Type), subPropertyAccessor, mappingContext, expressionCanBeNull, currentNamePart)); } return(null); }
private bool IsUnwrappingNeeded(ITypeSymbol targetType, MappingElement element) { return(targetType.Equals(element.ExpressionType) == false && (ObjectHelper.IsSimpleType(targetType) || SymbolHelper.IsNullable(targetType, out _))); }
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 bool IsConversionToSimpleTypeNeeded(ITypeSymbol targetType, ITypeSymbol sourceType) { return(targetType.Equals(sourceType) == false && (ObjectHelper.IsSimpleType(targetType) || SymbolHelper.IsNullable(targetType, out _))); }
private async Task <SyntaxNode> GetDefaultExpression(ITypeSymbol type, MappingContext mappingContext, MappingPath mappingPath) { if (mappingPath.AddToMapped(type) == false) { return(syntaxGenerator.DefaultExpression(type) .WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */"))); } if (SymbolHelper.IsNullable(type, out var underlyingType)) { type = underlyingType; } if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol) { var enumOption = namedTypeSymbol.MemberNames.Where(x => x != "value__" && x != ".ctor").OrderBy(x => x).FirstOrDefault(); if (enumOption != null) { return(SyntaxFactoryExtensions.CreateMemberAccessExpression(SyntaxFactory.IdentifierName(namedTypeSymbol.Name), false, enumOption)); } return(syntaxGenerator.DefaultExpression(type)); } if (type.SpecialType == SpecialType.None) { ObjectCreationExpressionSyntax objectCreationExpression = null; if (MappingHelper.IsCollection(type)) { var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type); if (type is IArrayTypeSymbol) { objectCreationExpression = CreateObject(type); } else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection) { if (type is INamedTypeSymbol namedType && namedType.IsGenericType) { var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x => syntaxGenerator.TypeExpression(x)))); var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax); objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); } else { var newType = SyntaxFactory.ParseTypeName("ArrayList"); objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); } } objectCreationExpression ??= CreateObject(type, Array.Empty <ArgumentSyntax>()); var subType = MappingHelper.GetElementType(type); var initializationBlockExpressions = new SeparatedSyntaxList <ExpressionSyntax>(); var subTypeDefault = (ExpressionSyntax)(await GetDefaultExpression(subType.Type, mappingContext, mappingPath.Clone()).ConfigureAwait(false)); if (subTypeDefault != null) { initializationBlockExpressions = initializationBlockExpressions.Add(subTypeDefault); } var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializationBlockExpressions).FixInitializerExpressionFormatting(objectCreationExpression); return(objectCreationExpression .WithInitializer(initializerExpressionSyntax) .WrapInReadonlyCollectionIfNecessary(isReadonlyCollection, syntaxGenerator)); } { var nt = type as INamedTypeSymbol; if (nt == null) { var genericTypeConstraints = type.UnwrapGeneric().ToList(); if (genericTypeConstraints.Any() == false) { return(GetDefaultForUnknown(type, ObjectType)); } nt = genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Class) as INamedTypeSymbol ?? genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Interface) as INamedTypeSymbol; } if (nt == null) { return(GetDefaultForUnknownType(type)); } if (nt.TypeKind == TypeKind.Interface) { var implementations = await SymbolFinder.FindImplementationsAsync(type, _document.Project.Solution).ConfigureAwait(false); var firstImplementation = implementations.FirstOrDefault(); if (firstImplementation is INamedTypeSymbol == false) { return(GetDefaultForUnknownType(type)); } nt = firstImplementation as INamedTypeSymbol; objectCreationExpression = CreateObject(nt); } else if (nt.TypeKind == TypeKind.Class && nt.IsAbstract) { var allDerived = await SymbolFinder.FindDerivedClassesAsync(nt, _document.Project.Solution).ConfigureAwait(false); var randomDerived = allDerived.FirstOrDefault(x => x.IsAbstract == false); if (randomDerived != null) { nt = randomDerived; objectCreationExpression = CreateObject(nt); } } else { var publicConstructors = nt.Constructors.Where(x => mappingContext.AccessibilityHelper.IsSymbolAccessible(x, nt)).ToList(); var hasDefaultConstructor = publicConstructors.Any(x => x.Parameters.Length == 0); if (hasDefaultConstructor == false && publicConstructors.Count > 0) { var randomConstructor = publicConstructors.First(); var constructorArguments = await GetConstructorArguments(mappingContext, mappingPath, randomConstructor).ConfigureAwait(false); objectCreationExpression = CreateObject(nt, constructorArguments); } } var fields = mappingTargetHelper.GetFieldsThaCanBeSetPublicly(nt, mappingContext); var assignments = new List <AssignmentExpressionSyntax>(fields.Count); foreach (var x in fields) { var identifier = (ExpressionSyntax)(SyntaxFactory.IdentifierName(x.Name)); var mappingSource = await this.FindMappingSource(x.Type, mappingContext, mappingPath.Clone()).ConfigureAwait(false); assignments.Add(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, identifier, mappingSource.Expression)); } if (objectCreationExpression == null) { objectCreationExpression = CreateObject(type); } return(SyntaxFactoryExtensions.WithMembersInitialization(objectCreationExpression, assignments)); } }
internal SyntaxNode GetDefaultExpression(ITypeSymbol type, MappingPath mappingPath) { if (mappingPath.AddToMapped(type) == false) { return(syntaxGenerator.DefaultExpression(type) .WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */"))); } if (SymbolHelper.IsNullable(type, out var underlyingType)) { type = underlyingType; } if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol) { var enumOptions = namedTypeSymbol.MemberNames.Where(x => x != "value__" && x != ".ctor").ToList(); if (enumOptions.Count > 0) { return(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(namedTypeSymbol.Name), syntaxGenerator.IdentifierName(enumOptions[0]))); } return(syntaxGenerator.DefaultExpression(type)); } if (type.SpecialType == SpecialType.None) { var objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(type); if (MappingHelper.IsCollection(type)) { var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type); if (type is IArrayTypeSymbol) { objectCreationExpression = SyntaxFactory.ObjectCreationExpression((TypeSyntax)syntaxGenerator.TypeExpression(type)); } else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection) { var namedType = type as INamedTypeSymbol; if (namedType.IsGenericType) { var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x => syntaxGenerator.TypeExpression(x)))); var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax); objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); } else { var newType = SyntaxFactory.ParseTypeName("ArrayList"); objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); } } var subType = MappingHelper.GetElementType(type); var initializationBlockExpressions = new SeparatedSyntaxList <ExpressionSyntax>(); var subTypeDefault = (ExpressionSyntax)GetDefaultExpression(subType, mappingPath.Clone()); if (subTypeDefault != null) { initializationBlockExpressions = initializationBlockExpressions.Add(subTypeDefault); } var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializationBlockExpressions).FixInitializerExpressionFormatting(objectCreationExpression); return(objectCreationExpression .WithInitializer(initializerExpressionSyntax) .WrapInReadonlyCollectionIfNecessary(isReadonlyCollection, syntaxGenerator)); } { var nt = type as INamedTypeSymbol; if (nt == null) { var genericTypeConstraints = type.UnwrapGeneric().ToList(); if (genericTypeConstraints.Any() == false) { return(GetDefaultForUnknown(type, SyntaxFactory.ParseTypeName("object"))); } nt = genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Class) as INamedTypeSymbol ?? genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Interface) as INamedTypeSymbol; } if (nt == null) { return(GetDefaultForUnknownType(type)); } if (nt.TypeKind == TypeKind.Interface) { var implementations = SymbolFinder.FindImplementationsAsync(type, this._document.Project.Solution).Result; var firstImplementation = implementations.FirstOrDefault(); if (firstImplementation is INamedTypeSymbol == false) { return(GetDefaultForUnknownType(type)); } nt = firstImplementation as INamedTypeSymbol; objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt); } else if (nt.TypeKind == TypeKind.Class && nt.IsAbstract) { var randomDerived = SymbolFinder.FindDerivedClassesAsync(nt, this._document.Project.Solution).Result .FirstOrDefault(x => x.IsAbstract == false); if (randomDerived != null) { nt = randomDerived; objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt); } } var publicConstructors = nt.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public || (x.DeclaredAccessibility == Accessibility.Internal && x.ContainingAssembly.IsSameAssemblyOrHasFriendAccessTo(_contextAssembly))).ToList(); var hasDefaultConstructor = publicConstructors.Any(x => x.Parameters.Length == 0); if (hasDefaultConstructor == false && publicConstructors.Count > 0) { var randomConstructor = publicConstructors.First(); var constructorArguments = randomConstructor.Parameters.Select(p => GetDefaultExpression(p.Type, mappingPath.Clone())).ToList(); objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt, constructorArguments); } var fields = ObjectHelper.GetFieldsThaCanBeSetPublicly(nt, _contextAssembly); var assignments = fields.Select(x => { var identifier = (ExpressionSyntax)(SyntaxFactory.IdentifierName(x.Name)); return((ExpressionSyntax)syntaxGenerator.AssignmentStatement(identifier, this.FindMappingSource(x.Type, mappingPath.Clone()).Expression)); }).ToList(); if (assignments.Count == 0) { return(objectCreationExpression); } var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, new SeparatedSyntaxList <ExpressionSyntax>().AddRange(assignments)).FixInitializerExpressionFormatting(objectCreationExpression); return(objectCreationExpression.WithInitializer(initializerExpressionSyntax)); } } return(GetDefaultForSpecialType(type)); }