예제 #1
0
        private void AddComplexDataHandler(
            CSharpSyntaxGeneratorSettings settings,
            ClassBuilder classBuilder,
            ConstructorBuilder constructorBuilder,
            MethodBuilder method,
            ComplexTypeDescriptor complexTypeDescriptor,
            HashSet <string> processed,
            bool isNonNullable)
        {
            if (complexTypeDescriptor.ParentRuntimeType is null)
            {
                throw new InvalidOperationException();
            }

            method
            .AddParameter(_dataParameterName)
            .SetType(complexTypeDescriptor.ParentRuntimeType
                     .ToString()
                     .MakeNullable(!isNonNullable))
            .SetName(_dataParameterName);

            if (settings.IsStoreEnabled())
            {
                method
                .AddParameter(_snapshot)
                .SetType(TypeNames.IEntityStoreSnapshot);
            }

            if (!isNonNullable)
            {
                method.AddCode(EnsureProperNullability(_dataParameterName, isNonNullable));
            }

            const string returnValue = nameof(returnValue);

            method.AddCode($"{complexTypeDescriptor.RuntimeType.Name}? {returnValue};");
            method.AddEmptyLine();

            GenerateIfForEachImplementedBy(
                method,
                complexTypeDescriptor,
                o => GenerateComplexDataInterfaceIfClause(settings, o, returnValue));

            method.AddCode($"return {returnValue};");

            AddRequiredMapMethods(
                settings,
                _dataParameterName,
                complexTypeDescriptor,
                classBuilder,
                constructorBuilder,
                processed);
        }
        private void AddUpdateEntityMethod(
            ClassBuilder classBuilder,
            MethodBuilder methodBuilder,
            INamedTypeDescriptor namedTypeDescriptor,
            HashSet <string> processed)
        {
            methodBuilder.AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"{TypeNames.EntityId} {_entityId}")
                .SetRighthandSide(
                    MethodCallBuilder
                    .Inline()
                    .SetMethodName(_idSerializer, "Parse")
                    .AddArgument($"{_obj}.Value")));

            methodBuilder.AddCode(
                MethodCallBuilder
                .New()
                .SetMethodName(_entityIds, nameof(List <object> .Add))
                .AddArgument(_entityId));

            methodBuilder.AddEmptyLine();

            if (namedTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
            {
                // If the type is an interface
                foreach (ObjectTypeDescriptor concreteType in interfaceTypeDescriptor.ImplementedBy)
                {
                    methodBuilder
                    .AddEmptyLine()
                    .AddCode(CreateUpdateEntityStatement(concreteType)
                             .AddCode($"return {_entityId};"));
                }

                methodBuilder.AddEmptyLine();
                methodBuilder.AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
            }
            else if (namedTypeDescriptor is ObjectTypeDescriptor objectTypeDescriptor)
            {
                BuildTryGetEntityIf(
                    CreateEntityType(
                        objectTypeDescriptor.Name,
                        objectTypeDescriptor.RuntimeType.NamespaceWithoutGlobal))
                .AddCode(CreateEntityConstructorCall(objectTypeDescriptor, false))
                .AddElse(CreateEntityConstructorCall(objectTypeDescriptor, true));

                methodBuilder.AddEmptyLine();
                methodBuilder.AddCode($"return {_entityId};");
            }

            AddRequiredDeserializeMethods(namedTypeDescriptor, classBuilder, processed);
        }
        private void AddEntityHandler(
            ClassBuilder classBuilder,
            ConstructorBuilder constructorBuilder,
            MethodBuilder method,
            ComplexTypeDescriptor complexTypeDescriptor,
            HashSet <string> processed,
            bool isNonNullable)
        {
            method
            .AddParameter(_entityId)
            .SetType(TypeNames.EntityId.MakeNullable(!isNonNullable));
            method
            .AddParameter(_snapshot)
            .SetType(TypeNames.IEntityStoreSnapshot);

            if (!isNonNullable)
            {
                method.AddCode(EnsureProperNullability(_entityId, isNonNullable));
            }

            if (complexTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
            {
                foreach (ObjectTypeDescriptor implementee in interfaceTypeDescriptor.ImplementedBy
                         .Where(x => x.IsEntity()))
                {
                    NameString dataMapperName =
                        CreateEntityMapperName(implementee.RuntimeType.Name, implementee.Name);

                    if (processed.Add(dataMapperName))
                    {
                        var dataMapperType =
                            TypeNames.IEntityMapper.WithGeneric(
                                CreateEntityType(
                                    implementee.Name,
                                    implementee.RuntimeType.NamespaceWithoutGlobal)
                                .ToString(),
                                implementee.RuntimeType.Name);

                        AddConstructorAssignedField(
                            dataMapperType,
                            GetFieldName(dataMapperName),
                            GetParameterName(dataMapperName),
                            classBuilder,
                            constructorBuilder);
                    }

                    method.AddCode(GenerateEntityHandlerIfClause(implementee, isNonNullable));
                }
            }

            method.AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
        }
        private void GenerateIfForEachImplementedBy(
            MethodBuilder method,
            ComplexTypeDescriptor complexTypeDescriptor,
            Func<ObjectTypeDescriptor, IfBuilder> generator)
        {
            if (!(complexTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor) ||
                !interfaceTypeDescriptor.ImplementedBy.Any())
            {
                return;
            }

            IEnumerable<ObjectTypeDescriptor> dataTypes =
                interfaceTypeDescriptor.ImplementedBy.Where(x => x.IsData());

            IfBuilder ifChain = generator(dataTypes.First());

            foreach (ObjectTypeDescriptor objectTypeDescriptor in dataTypes.Skip(1))
            {
                ifChain.AddIfElse(generator(objectTypeDescriptor).SkipIndents());
            }

            ifChain.AddElse(ExceptionBuilder.New(TypeNames.NotSupportedException));

            method.AddCode(ifChain);
        }
        private void AddDataTypeDeserializerMethod(
            ClassBuilder classBuilder,
            MethodBuilder methodBuilder,
            ComplexTypeDescriptor complexTypeDescriptor,
            HashSet <string> processed)
        {
            if (complexTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
            {
                AddInterfaceDataTypeDeserializerToMethod(methodBuilder, interfaceTypeDescriptor);
            }
            else
            {
                MethodCallBuilder returnStatement = MethodCallBuilder
                                                    .New()
                                                    .SetReturn()
                                                    .SetNew()
                                                    .SetMethodName(complexTypeDescriptor.Name);

                foreach (PropertyDescriptor property in complexTypeDescriptor.Properties)
                {
                    returnStatement.AddArgument(BuildUpdateMethodCall(property));
                }

                methodBuilder.AddCode(returnStatement);
            }

            AddRequiredDeserializeMethods(complexTypeDescriptor, classBuilder, processed);
        }
        private void AddInterfaceDataTypeDeserializerToMethod(
            MethodBuilder methodBuilder,
            InterfaceTypeDescriptor interfaceTypeDescriptor)
        {
            methodBuilder.AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {_typename}")
                .SetRighthandSide(MethodCallBuilder
                                  .Inline()
                                  .SetMethodName(
                                      _obj,
                                      "Value",
                                      nameof(JsonElement.GetProperty))
                                  .AddArgument(WellKnownNames.TypeName.AsStringToken())
                                  .Chain(x => x.SetMethodName(nameof(JsonElement.GetString)))));

            // If the type is an interface
            foreach (ObjectTypeDescriptor concreteType in interfaceTypeDescriptor.ImplementedBy)
            {
                MethodCallBuilder returnStatement = MethodCallBuilder
                                                    .New()
                                                    .SetReturn()
                                                    .SetNew()
                                                    .SetMethodName(
                    $"{concreteType.RuntimeType.Namespace}.State." +
                    CreateDataTypeName(concreteType.Name))
                                                    .AddArgument("typename");

                foreach (PropertyDescriptor property in concreteType.Properties)
                {
                    if (property.Name.Value.EqualsOrdinal(WellKnownNames.TypeName))
                    {
                        continue;
                    }

                    returnStatement.AddArgument(
                        CodeBlockBuilder
                        .New()
                        .AddCode($"{GetParameterName(property.Name)}: ")
                        .AddCode(BuildUpdateMethodCall(property)));
                }

                IfBuilder ifStatement = IfBuilder
                                        .New()
                                        .SetCondition(
                    $"typename?.Equals(\"{concreteType.Name}\", " +
                    $"{TypeNames.OrdinalStringComparison}) ?? false")
                                        .AddCode(returnStatement);

                methodBuilder
                .AddEmptyLine()
                .AddCode(ifStatement);
            }

            methodBuilder
            .AddEmptyLine()
            .AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
        }
예제 #7
0
        private void AddDataHandler(
            ClassBuilder classBuilder,
            ConstructorBuilder constructorBuilder,
            MethodBuilder method,
            ComplexTypeDescriptor namedTypeDescriptor,
            HashSet <string> processed,
            bool isNonNullable)
        {
            method
            .AddParameter(_dataParameterName)
            .SetType(namedTypeDescriptor.ParentRuntimeType !
                     .ToString()
                     .MakeNullable(!isNonNullable));
            method
            .AddParameter(_snapshot)
            .SetType(TypeNames.IEntityStoreSnapshot);

            if (!isNonNullable)
            {
                method.AddCode(EnsureProperNullability(_dataParameterName, isNonNullable));
            }

            const string returnValue = nameof(returnValue);

            method.AddCode($"{namedTypeDescriptor.RuntimeType.Name} {returnValue} = default!;");
            method.AddEmptyLine();

            GenerateIfForEachImplementedBy(
                method,
                namedTypeDescriptor,
                o => GenerateDataInterfaceIfClause(o, isNonNullable, returnValue));

            method.AddCode($"return {returnValue};");

            AddRequiredMapMethods(
                _dataParameterName,
                namedTypeDescriptor,
                classBuilder,
                constructorBuilder,
                processed);
        }
        private void AddScalarTypeDeserializerMethod(
            MethodBuilder methodBuilder,
            ILeafTypeDescriptor namedType)
        {
            string deserializeMethod = JsonUtils.GetParseMethod(namedType.SerializationType);

            methodBuilder.AddCode(
                MethodCallBuilder
                .New()
                .SetReturn()
                .SetMethodName(GetFieldName(namedType.Name) + "Parser", "Parse")
                .AddArgument(MethodCallBuilder
                             .Inline()
                             .SetMethodName(_obj, nameof(Nullable <JsonElement> .Value), deserializeMethod)
                             .SetNullForgiving()));
        }
        private void AddDeserializeMethod(
            ITypeDescriptor typeReference,
            ClassBuilder classBuilder,
            HashSet <string> processed)
        {
            string methodName = DeserializerMethodNameFromTypeName(typeReference);

            if (processed.Add(methodName))
            {
                MethodBuilder methodBuilder = classBuilder
                                              .AddMethod()
                                              .SetPrivate()
                                              .SetReturnType(typeReference.ToStateTypeReference())
                                              .SetName(methodName);


                if (typeReference.IsOrContainsEntityType())
                {
                    methodBuilder
                    .AddParameter(_session, x => x.SetType(TypeNames.IEntityStoreUpdateSession))
                    .AddParameter(_obj, x => x.SetType(TypeNames.JsonElement.MakeNullable()))
                    .AddParameter(
                        _entityIds,
                        x => x.SetType(TypeNames.ISet.WithGeneric(TypeNames.EntityId)));
                }
                else
                {
                    methodBuilder
                    .AddParameter(_obj)
                    .SetType(TypeNames.JsonElement.MakeNullable());
                }

                IfBuilder jsonElementNullCheck = IfBuilder
                                                 .New()
                                                 .SetCondition($"!{_obj}.HasValue")
                                                 .AddCode(
                    typeReference.IsNonNullableType()
                            ? ExceptionBuilder.New(TypeNames.ArgumentNullException)
                            : CodeLineBuilder.From("return null;"));

                methodBuilder
                .AddCode(jsonElementNullCheck)
                .AddEmptyLine();

                AddDeserializeMethodBody(classBuilder, methodBuilder, typeReference, processed);
            }
        }
예제 #10
0
        private void AddArrayHandler(
            ClassBuilder classBuilder,
            MethodBuilder methodBuilder,
            ListTypeDescriptor listTypeDescriptor,
            HashSet <string> processed)
        {
            var listVarName = GetParameterName(listTypeDescriptor.Name) + "s";

            methodBuilder
            .AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {listVarName}")
                .SetRighthandSide(
                    CodeBlockBuilder
                    .New()
                    .AddCode("new ")
                    .AddCode(TypeNames.List)
                    .AddCode("<")
                    .AddCode(
                        listTypeDescriptor.InnerType
                        .ToStateTypeReference()
                        .SkipTrailingSpace())
                    .AddCode(">")
                    .AddCode("()")))
            .AddEmptyLine()
            .AddCode(
                ForEachBuilder
                .New()
                .SetLoopHeader(
                    $"{TypeNames.JsonElement} {_child} in {_obj}.Value.EnumerateArray()")
                .AddCode(
                    MethodCallBuilder
                    .New()
                    .SetMethodName(listVarName, nameof(List <object> .Add))
                    .AddArgument(
                        BuildUpdateMethodCall(
                            listTypeDescriptor.InnerType,
                            CodeInlineBuilder.From(_child)))))
            .AddEmptyLine()
            .AddCode($"return {listVarName};");

            AddDeserializeMethod(listTypeDescriptor.InnerType, classBuilder, processed);
        }
예제 #11
0
        private void AddInterfaceDataTypeDeserializerToMethod(
            MethodBuilder methodBuilder,
            InterfaceTypeDescriptor interfaceTypeDescriptor)
        {
            methodBuilder.AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {_typename}")
                .SetRighthandSide(MethodCallBuilder
                                  .Inline()
                                  .SetMethodName(
                                      _obj,
                                      "Value",
                                      nameof(JsonElement.GetProperty))
                                  .AddArgument(WellKnownNames.TypeName.AsStringToken())
                                  .Chain(x => x.SetMethodName(nameof(JsonElement.GetString)))));

            // If the type is an interface
            foreach (ObjectTypeDescriptor concreteType in interfaceTypeDescriptor.ImplementedBy)
            {
                MethodCallBuilder returnStatement = CreateBuildDataStatement(concreteType)
                                                    .SetReturn();

                IfBuilder ifStatement = IfBuilder
                                        .New()
                                        .SetCondition(
                    $"typename?.Equals(\"{concreteType.Name}\", " +
                    $"{TypeNames.OrdinalStringComparison}) ?? false")
                                        .AddCode(returnStatement);

                methodBuilder
                .AddEmptyLine()
                .AddCode(ifStatement);
            }

            methodBuilder
            .AddEmptyLine()
            .AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
        }
        private void AddScalarTypeDeserializerMethod(
            MethodBuilder methodBuilder,
            ILeafTypeDescriptor namedType)
        {
            MethodCallBuilder methodCall = MethodCallBuilder
                                           .New()
                                           .SetReturn()
                                           .SetMethodName(GetFieldName(namedType.Name) + "Parser", "Parse");

            if (namedType.SerializationType.ToString() == TypeNames.JsonElement)
            {
                methodCall.AddArgument($"{_obj}.{nameof(Nullable<JsonElement>.Value)}!");
            }
            else
            {
                var deserializeMethod = JsonUtils.GetParseMethod(namedType.SerializationType);
                methodCall.AddArgument(MethodCallBuilder
                                       .Inline()
                                       .SetMethodName(_obj, nameof(Nullable <JsonElement> .Value), deserializeMethod)
                                       .SetNullForgiving());
            }

            methodBuilder.AddCode(methodCall);
        }
예제 #13
0
        private void AddArrayHandler(
            CSharpSyntaxGeneratorSettings settings,
            ClassBuilder classBuilder,
            ConstructorBuilder constructorBuilder,
            MethodBuilder methodBuilder,
            ListTypeDescriptor listTypeDescriptor,
            HashSet <string> processed,
            bool isNonNullable)
        {
            methodBuilder
            .AddParameter(_list)
            .SetType(listTypeDescriptor.ToStateTypeReference());

            if (settings.IsStoreEnabled())
            {
                methodBuilder
                .AddParameter(_snapshot)
                .SetType(TypeNames.IEntityStoreSnapshot);
            }

            var listVarName = GetParameterName(listTypeDescriptor.Name) + "s";

            methodBuilder.AddCode(EnsureProperNullability(_list, isNonNullable));

            methodBuilder.AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {listVarName}")
                .SetRighthandSide(
                    CodeBlockBuilder
                    .New()
                    .AddCode("new ")
                    .AddCode(TypeNames.List)
                    .AddCode("<")
                    .AddCode(
                        listTypeDescriptor.InnerType.ToTypeReference().SkipTrailingSpace())
                    .AddCode(">")
                    .AddCode("()")));
            methodBuilder.AddEmptyLine();

            ForEachBuilder forEachBuilder = ForEachBuilder
                                            .New()
                                            .SetLoopHeader(
                CodeBlockBuilder
                .New()
                .AddCode(listTypeDescriptor.InnerType.ToStateTypeReference())
                .AddCode($"{_child} in {_list}"))
                                            .AddCode(
                MethodCallBuilder
                .New()
                .SetMethodName(listVarName, nameof(List <object> .Add))
                .AddArgument(MethodCallBuilder
                             .Inline()
                             .SetMethodName(MapMethodNameFromTypeName(listTypeDescriptor.InnerType))
                             .AddArgument(_child)
                             .If(settings.IsStoreEnabled(), x => x.AddArgument(_snapshot))));

            methodBuilder
            .AddCode(forEachBuilder)
            .AddEmptyLine()
            .AddCode($"return {listVarName};");

            AddMapMethod(
                settings,
                listVarName,
                listTypeDescriptor.InnerType,
                classBuilder,
                constructorBuilder,
                processed);
        }
예제 #14
0
        private void AddEntityDataTypeDeserializerToMethod(
            MethodBuilder methodBuilder,
            InterfaceTypeDescriptor interfaceTypeDescriptor)
        {
            methodBuilder.AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {_typename}")
                .SetRighthandSide(MethodCallBuilder
                                  .Inline()
                                  .SetMethodName(
                                      _obj,
                                      "Value",
                                      nameof(JsonElement.GetProperty))
                                  .AddArgument(WellKnownNames.TypeName.AsStringToken())
                                  .Chain(x => x.SetMethodName(nameof(JsonElement.GetString)))));

            foreach (ObjectTypeDescriptor concreteType in interfaceTypeDescriptor.ImplementedBy)
            {
                ICode builder;
                if (concreteType.IsEntity())
                {
                    builder = CodeBlockBuilder
                              .New()
                              .AddCode(
                        AssignmentBuilder
                        .New()
                        .SetLefthandSide($"{TypeNames.EntityId} {_entityId}")
                        .SetRighthandSide(
                            MethodCallBuilder
                            .Inline()
                            .SetMethodName(_idSerializer, "Parse")
                            .AddArgument($"{_obj}.Value")))
                              .AddCode(CreateUpdateEntityStatement(concreteType)
                                       .AddCode(MethodCallBuilder
                                                .New()
                                                .SetReturn()
                                                .SetNew()
                                                .SetMethodName(TypeNames.EntityIdOrData)
                                                .AddArgument(_entityId)));
                }
                else
                {
                    builder =
                        MethodCallBuilder
                        .New()
                        .SetNew()
                        .SetReturn()
                        .SetMethodName(TypeNames.EntityIdOrData)
                        .AddArgument(CreateBuildDataStatement(concreteType)
                                     .SetDetermineStatement(false)
                                     .SetNew());
                }

                methodBuilder
                .AddEmptyLine()
                .AddCode(IfBuilder
                         .New()
                         .SetCondition(
                             $"typename?.Equals(\"{concreteType.Name}\", " +
                             $"{TypeNames.OrdinalStringComparison}) ?? false")
                         .AddCode(builder));
            }

            methodBuilder
            .AddEmptyLine()
            .AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
        }
        protected override void Generate(ITypeDescriptor typeDescriptor,
                                         CSharpSyntaxGeneratorSettings settings,
                                         CodeWriter writer,
                                         out string fileName,
                                         out string?path,
                                         out string ns)
        {
            ComplexTypeDescriptor descriptor =
                typeDescriptor as ComplexTypeDescriptor ??
                throw new InvalidOperationException(
                          "A result data factory can only be generated for complex types");

            fileName = CreateResultFactoryName(descriptor.RuntimeType.Name);
            path     = State;
            ns       = CreateStateNamespace(descriptor.RuntimeType.NamespaceWithoutGlobal);

            ClassBuilder classBuilder =
                ClassBuilder
                .New()
                .SetName(fileName)
                .AddImplements(
                    TypeNames.IOperationResultDataFactory.WithGeneric(descriptor.RuntimeType));

            ConstructorBuilder constructorBuilder = classBuilder
                                                    .AddConstructor()
                                                    .SetTypeName(descriptor.Name);

            if (settings.IsStoreEnabled())
            {
                AddConstructorAssignedField(
                    TypeNames.IEntityStore,
                    _entityStore,
                    entityStore,
                    classBuilder,
                    constructorBuilder);
            }

            MethodCallBuilder returnStatement = MethodCallBuilder
                                                .New()
                                                .SetReturn()
                                                .SetNew()
                                                .SetMethodName(descriptor.RuntimeType.Name);

            foreach (PropertyDescriptor property in descriptor.Properties)
            {
                returnStatement
                .AddArgument(BuildMapMethodCall(settings, _info, property));
            }

            IfBuilder ifHasCorrectType = IfBuilder
                                         .New()
                                         .SetCondition(
                $"{_dataInfo} is {CreateResultInfoName(descriptor.RuntimeType.Name)} {_info}")
                                         .AddCode(returnStatement);

            MethodBuilder createMethod = classBuilder
                                         .AddMethod("Create")
                                         .SetAccessModifier(AccessModifier.Public)
                                         .SetReturnType(descriptor.RuntimeType.Name)
                                         .AddParameter(_dataInfo, b => b.SetType(TypeNames.IOperationResultDataInfo))
                                         .AddParameter(
                _snapshot,
                b => b.SetDefault("null")
                .SetType(TypeNames.IEntityStoreSnapshot.MakeNullable()));

            if (settings.IsStoreEnabled())
            {
                createMethod
                .AddCode(
                    IfBuilder.New()
                    .SetCondition($"{_snapshot} is null")
                    .AddCode(
                        AssignmentBuilder
                        .New()
                        .SetLefthandSide(_snapshot)
                        .SetRighthandSide($"{_entityStore}.CurrentSnapshot")))
                .AddEmptyLine();
            }

            createMethod.AddCode(ifHasCorrectType)
            .AddEmptyLine()
            .AddCode(
                ExceptionBuilder
                .New(TypeNames.ArgumentException)
                .AddArgument(
                    $"\"{CreateResultInfoName(descriptor.RuntimeType.Name)} expected.\""));

            var processed = new HashSet <string>();

            AddRequiredMapMethods(
                settings,
                _info,
                descriptor,
                classBuilder,
                constructorBuilder,
                processed,
                true);

            classBuilder
            .AddProperty("ResultType")
            .SetType(TypeNames.Type)
            .AsLambda($"typeof({descriptor.RuntimeType.Namespace}.{descriptor.Implements[0]})")
            .SetInterface(TypeNames.IOperationResultDataFactory);

            classBuilder
            .AddMethod("Create")
            .SetInterface(TypeNames.IOperationResultDataFactory)
            .SetReturnType(TypeNames.Object)
            .AddParameter(_dataInfo, b => b.SetType(TypeNames.IOperationResultDataInfo))
            .AddParameter(
                _snapshot,
                b => b.SetType(TypeNames.IEntityStoreSnapshot.MakeNullable()))
            .AddCode(
                MethodCallBuilder
                .New()
                .SetReturn()
                .SetMethodName("Create")
                .AddArgument(_dataInfo)
                .AddArgument(_snapshot));

            classBuilder.Build(writer);
        }
        private void AddBuildDataMethod(
            CSharpSyntaxGeneratorSettings settings,
            InterfaceTypeDescriptor resultNamedType,
            ClassBuilder classBuilder)
        {
            var concreteType =
                CreateResultInfoName(
                    resultNamedType.ImplementedBy.First().RuntimeType.Name);

            MethodBuilder buildDataMethod = classBuilder
                                            .AddMethod()
                                            .SetPrivate()
                                            .SetName("BuildData")
                                            .SetReturnType($"({resultNamedType.RuntimeType.Name}, {concreteType})")
                                            .AddParameter(_obj, x => x.SetType(TypeNames.JsonElement));

            if (settings.IsStoreEnabled())
            {
                buildDataMethod.AddCode(
                    AssignmentBuilder
                    .New()
                    .SetLefthandSide($"var {_entityIds}")
                    .SetRighthandSide(MethodCallBuilder
                                      .Inline()
                                      .SetNew()
                                      .SetMethodName(TypeNames.HashSet)
                                      .AddGeneric(TypeNames.EntityId)))
                .AddCode(
                    AssignmentBuilder
                    .New()
                    .SetLefthandSide($"{TypeNames.IEntityStoreSnapshot} {_snapshot}")
                    .SetRighthandSide("default!"));
            }

            buildDataMethod.AddEmptyLine();


            CodeBlockBuilder storeUpdateBody = CodeBlockBuilder.New();

            if (settings.IsStoreEnabled())
            {
                foreach (PropertyDescriptor property in
                         resultNamedType.Properties.Where(prop => prop.Type.IsOrContainsEntity()))
                {
                    var variableName = $"{GetParameterName(property.Name)}Id";

                    buildDataMethod
                    .AddCode(AssignmentBuilder
                             .New()
                             .SetLefthandSide(CodeBlockBuilder
                                              .New()
                                              .AddCode(property.Type.ToStateTypeReference())
                                              .AddCode(variableName))
                             .SetRighthandSide("default!"));

                    storeUpdateBody
                    .AddCode(AssignmentBuilder
                             .New()
                             .SetLefthandSide(variableName)
                             .SetRighthandSide(BuildUpdateMethodCall(property)));
                }

                storeUpdateBody
                .AddEmptyLine()
                .AddCode(AssignmentBuilder
                         .New()
                         .SetLefthandSide(_snapshot)
                         .SetRighthandSide($"{_session}.CurrentSnapshot"));

                buildDataMethod
                .AddCode(MethodCallBuilder
                         .New()
                         .SetMethodName(_entityStore, "Update")
                         .AddArgument(LambdaBuilder
                                      .New()
                                      .AddArgument(_session)
                                      .SetBlock(true)
                                      .SetCode(storeUpdateBody)));
            }

            buildDataMethod
            .AddEmptyLine()
            .AddCode(
                AssignmentBuilder
                .New()
                .SetLefthandSide($"var {_resultInfo}")
                .SetRighthandSide(
                    CreateResultInfoMethodCall(settings, resultNamedType, concreteType)))
            .AddEmptyLine()
            .AddCode(
                TupleBuilder
                .Inline()
                .SetDetermineStatement(true)
                .SetReturn()
                .AddMember(MethodCallBuilder
                           .Inline()
                           .SetMethodName(_resultDataFactory, "Create")
                           .AddArgument(_resultInfo))
                .AddMember(_resultInfo));
        }
        protected override void Generate(ITypeDescriptor typeDescriptor,
                                         CSharpSyntaxGeneratorSettings settings,
                                         CodeWriter writer,
                                         out string fileName,
                                         out string?path,
                                         out string ns)
        {
            // Setup class
            ComplexTypeDescriptor descriptor =
                typeDescriptor as ComplexTypeDescriptor ??
                throw new InvalidOperationException(
                          "A result entity mapper can only be generated for complex types");

            fileName = descriptor.ExtractMapperName();
            path     = State;
            ns       = CreateStateNamespace(descriptor.RuntimeType.NamespaceWithoutGlobal);

            ClassBuilder classBuilder = ClassBuilder
                                        .New()
                                        .AddImplements(
                TypeNames.IEntityMapper
                .WithGeneric(
                    descriptor.ExtractType().ToString(),
                    descriptor.RuntimeType.Name))
                                        .SetName(fileName);

            ConstructorBuilder constructorBuilder = ConstructorBuilder
                                                    .New()
                                                    .SetTypeName(descriptor.Name);

            AddConstructorAssignedField(
                TypeNames.IEntityStore,
                _entityStore,
                entityStore,
                classBuilder,
                constructorBuilder);

            // Define map method
            MethodBuilder mapMethod = MethodBuilder
                                      .New()
                                      .SetName(_map)
                                      .SetAccessModifier(AccessModifier.Public)
                                      .SetReturnType(descriptor.RuntimeType.Name)
                                      .AddParameter(
                ParameterBuilder
                .New()
                .SetType(
                    descriptor.Kind == TypeKind.Entity
                                ? CreateEntityType(
                        descriptor.Name,
                        descriptor.RuntimeType.NamespaceWithoutGlobal)
                    .ToString()
                                : descriptor.Name)
                .SetName(_entity))
                                      .AddParameter(
                _snapshot,
                b => b.SetDefault("null")
                .SetType(TypeNames.IEntityStoreSnapshot.MakeNullable()));

            mapMethod
            .AddCode(IfBuilder
                     .New()
                     .SetCondition($"{_snapshot} is null")
                     .AddCode(AssignmentBuilder
                              .New()
                              .SetLefthandSide(_snapshot)
                              .SetRighthandSide($"{_entityStore}.CurrentSnapshot")))
            .AddEmptyLine();

            MethodCallBuilder constructorCall =
                MethodCallBuilder
                .New()
                .SetReturn()
                .SetNew()
                .SetMethodName(descriptor.RuntimeType.Name);

            if (typeDescriptor is ComplexTypeDescriptor complexTypeDescriptor)
            {
                foreach (PropertyDescriptor property in complexTypeDescriptor.Properties)
                {
                    constructorCall.AddArgument(BuildMapMethodCall(settings, _entity, property));
                }
            }

            mapMethod.AddCode(constructorCall);

            if (constructorBuilder.HasParameters())
            {
                classBuilder.AddConstructor(constructorBuilder);
            }

            classBuilder.AddMethod(mapMethod);

            AddRequiredMapMethods(
                settings,
                _entity,
                descriptor,
                classBuilder,
                constructorBuilder,
                new HashSet <string>());

            classBuilder.Build(writer);
        }
        private void AddEntityOrUnionDataHandler(
            CSharpSyntaxGeneratorSettings settings,
            ClassBuilder classBuilder,
            ConstructorBuilder constructorBuilder,
            MethodBuilder method,
            ComplexTypeDescriptor complexTypeDescriptor,
            HashSet <string> processed,
            bool isNonNullable)
        {
            method
            .AddParameter(_dataParameterName)
            .SetType(TypeNames.EntityIdOrData.MakeNullable(!isNonNullable))
            .SetName(_dataParameterName);

            method
            .AddParameter(_snapshot)
            .SetType(TypeNames.IEntityStoreSnapshot)
            .SetName(_snapshot);

            if (!isNonNullable)
            {
                method.AddCode(EnsureProperNullability(_dataParameterName, isNonNullable));
            }

            var dataHandlerMethodName =
                MapMethodNameFromTypeName(complexTypeDescriptor) + "Entity";

            MethodBuilder complexDataHandler = MethodBuilder
                                               .New()
                                               .SetReturnType(
                complexTypeDescriptor.RuntimeType.ToString().MakeNullable(!isNonNullable))
                                               .SetName(dataHandlerMethodName);

            AddComplexDataHandler(settings,
                                  classBuilder,
                                  constructorBuilder,
                                  complexDataHandler,
                                  complexTypeDescriptor,
                                  processed,
                                  isNonNullable);

            classBuilder.AddMethod(complexDataHandler);

            var entityDataHandlerMethodName =
                MapMethodNameFromTypeName(complexTypeDescriptor) + "Data";

            MethodBuilder entityDataHandler = MethodBuilder
                                              .New()
                                              .SetReturnType(
                complexTypeDescriptor.RuntimeType.ToString().MakeNullable(!isNonNullable))
                                              .SetName(entityDataHandlerMethodName);

            AddEntityHandler(
                classBuilder,
                constructorBuilder,
                entityDataHandler,
                complexTypeDescriptor,
                processed,
                isNonNullable);

            classBuilder.AddMethod(entityDataHandler);

            method.AddEmptyLine();

            var parameterName = isNonNullable ? _dataParameterName : $"{_dataParameterName}.Value";

            IfBuilder ifBuilder = IfBuilder
                                  .New()
                                  .SetCondition($"{parameterName}.EntityId is {{ }} id")
                                  .AddCode(MethodCallBuilder
                                           .New()
                                           .SetReturn()
                                           .SetMethodName(entityDataHandlerMethodName)
                                           .AddArgument("id")
                                           .AddArgument(_snapshot))
                                  .AddIfElse(IfBuilder
                                             .New()
                                             .SetCondition(
                                                 $"{parameterName}.Data is {complexTypeDescriptor.ParentRuntimeType!} d")
                                             .AddCode(MethodCallBuilder
                                                      .New()
                                                      .SetReturn()
                                                      .SetMethodName(dataHandlerMethodName)
                                                      .AddArgument("d")
                                                      .AddArgument(_snapshot)))
                                  .AddElse(ExceptionBuilder.New(TypeNames.ArgumentOutOfRangeException));

            method.AddCode(ifBuilder);

            AddRequiredMapMethods(
                settings,
                _dataParameterName,
                complexTypeDescriptor,
                classBuilder,
                constructorBuilder,
                processed);
        }