/
MembersContainerBuilderContext.cs
105 lines (88 loc) · 4.49 KB
/
MembersContainerBuilderContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;
namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext;
/// <summary>
/// An <see cref="IMembersContainerBuilderContext{T}"/> implementation.
/// </summary>
/// <typeparam name="T">The type of mapping.</typeparam>
public class MembersContainerBuilderContext<T>(MappingBuilderContext builderContext, T mapping)
: MembersMappingBuilderContext<T>(builderContext, mapping),
IMembersContainerBuilderContext<T>
where T : IMemberAssignmentTypeMapping
{
private readonly Dictionary<MemberPath, MemberNullDelegateAssignmentMapping> _nullDelegateMappings = new();
public void AddMemberAssignmentMapping(IMemberAssignmentMapping memberMapping) => AddMemberAssignmentMapping(Mapping, memberMapping);
public void AddNullDelegateMemberAssignmentMapping(IMemberAssignmentMapping memberMapping)
{
var nullConditionSourcePath = new MemberPath(memberMapping.SourcePath.PathWithoutTrailingNonNullable().ToList());
var container = GetOrCreateNullDelegateMappingForPath(nullConditionSourcePath);
AddMemberAssignmentMapping(container, memberMapping);
// set target member to null if null assignments are allowed
// and the source is null
if (BuilderContext.Configuration.Mapper.AllowNullPropertyAssignment && memberMapping.TargetPath.Member.Type.IsNullable())
{
container.AddNullMemberAssignment(SetterMemberPath.Build(BuilderContext, memberMapping.TargetPath));
}
}
private void AddMemberAssignmentMapping(IMemberAssignmentMappingContainer container, IMemberAssignmentMapping mapping)
{
SetSourceMemberMapped(mapping.SourcePath);
AddNullMemberInitializers(container, mapping.TargetPath);
container.AddMemberMapping(mapping);
}
private void AddNullMemberInitializers(IMemberAssignmentMappingContainer container, MemberPath path)
{
foreach (var nullableTrailPath in path.ObjectPathNullableSubPaths())
{
var nullablePath = new MemberPath(nullableTrailPath);
var type = nullablePath.Member.Type;
if (!nullablePath.Member.CanSet)
continue;
if (!BuilderContext.SymbolAccessor.HasDirectlyAccessibleParameterlessConstructor(type))
{
BuilderContext.ReportDiagnostic(DiagnosticDescriptors.NoParameterlessConstructorFound, type);
continue;
}
var setterNullablePath = SetterMemberPath.Build(BuilderContext, nullablePath);
if (setterNullablePath.IsMethod)
{
var getterNullablePath = GetterMemberPath.Build(BuilderContext, nullablePath);
container.AddMemberMappingContainer(
new MethodMemberNullAssignmentInitializerMapping(setterNullablePath, getterNullablePath)
);
continue;
}
container.AddMemberMappingContainer(new MemberNullAssignmentInitializerMapping(setterNullablePath));
}
}
private MemberNullDelegateAssignmentMapping GetOrCreateNullDelegateMappingForPath(MemberPath nullConditionSourcePath)
{
// if there is already an exact match return that
if (_nullDelegateMappings.TryGetValue(nullConditionSourcePath, out var mapping))
return mapping;
IMemberAssignmentMappingContainer parentMapping = Mapping;
// try to reuse parent path mappings and wrap inside them
// if the parentMapping is the first nullable path, no need to access the path in the condition in a null-safe way.
var needsNullSafeAccess = false;
foreach (var nullablePath in nullConditionSourcePath.ObjectPathNullableSubPaths().Reverse())
{
if (_nullDelegateMappings.TryGetValue(new MemberPath(nullablePath), out var parentMappingHolder))
{
parentMapping = parentMappingHolder;
break;
}
needsNullSafeAccess = true;
}
mapping = new MemberNullDelegateAssignmentMapping(
GetterMemberPath.Build(BuilderContext, nullConditionSourcePath),
parentMapping,
BuilderContext.Configuration.Mapper.ThrowOnPropertyMappingNullMismatch,
needsNullSafeAccess
);
_nullDelegateMappings[nullConditionSourcePath] = mapping;
parentMapping.AddMemberMappingContainer(mapping);
return mapping;
}
}