private MappingElement CreateMappingElementFromExtensionMethod(ITypeSymbol targetType, string methodName)
        {
            var sourceMethodAccessor = SyntaxFactoryExtensions.CreateMethodAccessExpression((ExpressionSyntax)sourceGlobalAccessor, sourceType.CanBeNull, methodName);

            return(new MappingElement()
            {
                Expression = sourceMethodAccessor,
                ExpressionType = new AnnotatedType(targetType)
            });
        }
Example #2
0
        private MappingElement TryFindSource(string targetName, MappingContext mappingContext, AnnotatedType accessedVia)
        {
            //Direct 1-1 mapping
            var matchedSourceProperty = sourceProperties.Value
                                        .Where(x => x.Name.Equals(targetName, StringComparison.OrdinalIgnoreCase) || $"{potentialPrefix}{x.Name}".Equals(targetName, StringComparison.OrdinalIgnoreCase))
                                        .FirstOrDefault(p => p.CanBeGet(accessedVia.Type, mappingContext));

            if (matchedSourceProperty != null)
            {
                return(new MappingElement()
                {
                    Expression = SyntaxFactoryExtensions.CreateMemberAccessExpression((ExpressionSyntax)sourceGlobalAccessor, accessedVia.CanBeNull, matchedSourceProperty.Name),
                    ExpressionType = new AnnotatedType(matchedSourceProperty.Type.Type, accessedVia.CanBeNull || matchedSourceProperty.Type.CanBeNull)
                });
            }

            //Non-direct (mapping like y.UserName = x.User.Name)
            var source = FindSubPropertySource(targetName, sourceType.Type, sourceProperties.Value, sourceGlobalAccessor, mappingContext, accessedVia.CanBeNull);

            if (source != null)
            {
                return(source);
            }

            //Flattening with function eg. t.Total = s.GetTotal()
            var matchedSourceMethod = sourceMethods.Value.Where((x => x.Name.EndsWith(targetName, StringComparison.OrdinalIgnoreCase))).FirstOrDefault(m => mappingContext.AccessibilityHelper.IsSymbolAccessible(m, accessedVia.Type));

            if (matchedSourceMethod != null)
            {
                var sourceMethodAccessor = SyntaxFactoryExtensions.CreateMethodAccessExpression((ExpressionSyntax)sourceGlobalAccessor, sourceType.CanBeNull, matchedSourceMethod.Name);
                return(new MappingElement()
                {
                    Expression = sourceMethodAccessor,
                    ExpressionType = new AnnotatedType(matchedSourceMethod.ReturnType, sourceType.CanBeNull || matchedSourceMethod.CanBeNull())
                });
            }

            // HIGHLY SPECULATIVE: Expanding acronyms: UserName = u.Name
            if (string.IsNullOrWhiteSpace(potentialPrefix) == false && potentialPrefix == potentialPrefix.ToLowerInvariant() && targetName != potentialPrefix)
            {
                var acronym = GetAcronym(targetName).ToLowerInvariant();
                if (acronym.StartsWith(potentialPrefix))
                {
                    var rest      = acronymPattern.Split(targetName).Skip(potentialPrefix.Length);
                    var newTarget = $"{potentialPrefix}{string.Join("", rest)}";
                    return(TryFindSource(newTarget, mappingContext, accessedVia));
                }
            }
            return(null);
        }
        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));
                            }
Example #4
0
        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 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));
                }
            }