private BlockSyntax PopulateRequiredConstructorBodyInitializers(BlockSyntax body, TransportModelEntity entityModel)
        {
            if (entityModel.TsDiscriminant is TransportModelEntityTsDiscriminantSyntaxKind kindDiscriminant)
            {
                body = body.AddStatements(
                    ExpressionStatement(
                        AssignmentExpression(
                            SyntaxKind.SimpleAssignmentExpression,
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                ThisExpression(),
                                IdentifierName("kind")),
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                IdentifierName($"{this.settings.CsTransportModelNamespace}.SyntaxKind"),
                                IdentifierName(kindDiscriminant.SyntaxKindValueName)))));
            }

            foreach (var property in entityModel.Members)
            {
                if (CsEmitterHelper.IsNodeCollection(property.Value.Type))
                {
                    body = body.AddStatements(
                        ExpressionStatement(
                            AssignmentExpression(
                                SyntaxKind.SimpleAssignmentExpression,
                                MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    ThisExpression(),
                                    IdentifierName(NameHelper.GetSafeVariableName(property.Key))),
                                ObjectCreationExpression(
                                    ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(property.Value, this.settings)),
                                    ArgumentList(SeparatedList <ArgumentSyntax>(new[] { Argument(ThisExpression()) })),
                                    null))));
                }
            }

            return(body);
        }
        private IEnumerable <ParameterSyntax> GetConstructorParameters(TransportModelEntity entityModel, out List <StatementSyntax> propertyInitializers, out List <ArgumentSyntax> baseConstructorArguments)
        {
            List <ParameterSyntax> result       = new List <ParameterSyntax>();
            List <StatementSyntax> initializers = new List <StatementSyntax>();
            List <ArgumentSyntax>  baseArgs     = new List <ArgumentSyntax>();

            var members     = CsEmitterHelper.GetMembers(entityModel);
            var baseMembers = entityModel.BaseEntity != null
                ? CsEmitterHelper.GetMembers(entityModel.BaseEntity.TransportModelItem, entityModel.BaseEntity.GenericArguments)
                : new Dictionary <string, TransportModelEntityMember>();

            foreach (var member in members)
            {
                if (member.Key != "kind")
                {
                    result.Add(Parameter(List <AttributeListSyntax>(), TokenList(), ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(member.Value, this.settings, true)), Identifier(NameHelper.GetSafeVariableName(member.Key)), null));

                    if (baseMembers.ContainsKey(member.Key))
                    {
                        baseArgs.Add(Argument(IdentifierName(NameHelper.GetSafeVariableName(member.Key))));
                    }
                    else
                    {
                        if (CsEmitterHelper.IsNodeCollection(member.Value.Type))
                        {
                            initializers.Add(
                                ExpressionStatement(
                                    InvocationExpression(
                                        MemberAccessExpression(
                                            SyntaxKind.SimpleMemberAccessExpression,
                                            MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(NameHelper.GetSafeVariableName(member.Key))),
                                            IdentifierName("AddRange")),
                                        ArgumentList(SeparatedList <ArgumentSyntax>(new[] { Argument(IdentifierName(NameHelper.GetSafeVariableName(member.Key))) })))));
                        }
                        else
                        {
                            initializers.Add(
                                ExpressionStatement(
                                    AssignmentExpression(
                                        SyntaxKind.SimpleAssignmentExpression,
                                        MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(NameHelper.GetSafeVariableName(member.Key))),
                                        IdentifierName(NameHelper.GetSafeVariableName(member.Key)))));
                        }
                    }
                }
            }

            if (entityModel.TsDiscriminant is TransportModelEntityTsDiscriminantBrand brandDiscriminant)
            {
                var member = entityModel.GetMemberByName(brandDiscriminant.BrandPropertyName);
                if (member != null)
                {
                    initializers.Add(
                        ExpressionStatement(
                            AssignmentExpression(
                                SyntaxKind.SimpleAssignmentExpression,
                                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(NameHelper.GetSafeVariableName(member.Name))),
                                ObjectCreationExpression(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(member, this.settings)), ArgumentList(), null))));
                }
            }

            propertyInitializers     = initializers;
            baseConstructorArguments = baseArgs;
            return(result);
        }
        private void EmitInterface(TransportModelInterface model, ICodeStream output)
        {
            var outputFile = (CodeFileCSharp)output.CreateCodeFile($"{CsEmitterHelper.GetCSharpModelShortName(model, ModelType.Ast)}.cs");

            var unit = CompilationUnit();

            unit = unit.AddUsings(
                UsingDirective(ParseName(nameof(System))));

            var entityInterface = InterfaceDeclaration(CsEmitterHelper.GetCSharpModelShortName(model, ModelType.Ast));

            entityInterface = entityInterface.AddModifiers(Token(SyntaxKind.PublicKeyword));

            if (model.GenericParameters.Count > 0)
            {
                entityInterface = entityInterface.WithTypeParameterList(TypeParameterList(SeparatedList <TypeParameterSyntax>(model.GenericParameters.Select(p => TypeParameter(p.Key)))));

                entityInterface = entityInterface.WithConstraintClauses(List <TypeParameterConstraintClauseSyntax>(
                                                                            model.GenericParameters.Select(p =>
                                                                                                           TypeParameterConstraintClause(
                                                                                                               IdentifierName(p.Key),
                                                                                                               SeparatedList <TypeParameterConstraintSyntax>(new[] { TypeConstraint(ParseTypeName(CsEmitterHelper.GetModelGenericParameterConstraintTypeName(p.Value, ModelType.Ast))) })))));
            }

            List <string> baseTypes = new List <string>();

            if (model.Name == this.settings.AstNodeBaseTypeQualified.Split('.').Last())
            {
                baseTypes.Add($"{settings.CsAstModelNamespace}.{baseNodeTypeInterface}");
            }

            foreach (var interfaceModel in model.Interfaces)
            {
                baseTypes.Add(CsEmitterHelper.GetCSharpModelFullyQualifiedName(interfaceModel, this.settings, ModelType.Ast));
            }

            if (baseTypes.Count > 0)
            {
                entityInterface = entityInterface.WithBaseList(BaseList(SeparatedList <BaseTypeSyntax>(baseTypes.Select(t => SimpleBaseType(ParseTypeName(t))))));
            }

            entityInterface = entityInterface.WithMembers(List <MemberDeclarationSyntax>(model.Members.Select(m =>
                                                                                                              CsEmitterHelper.IsNodeCollection(m.Value.Type) ?
                                                                                                              PropertyDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(m.Value, this.settings)), NameHelper.GetSafeVariableName(m.Value.Name))
                                                                                                              .WithAccessorList(
                                                                                                                  SyntaxFactory.AccessorList(
                                                                                                                      SyntaxFactory.List <AccessorDeclarationSyntax>(new AccessorDeclarationSyntax[]
            {
                SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
            })))
                :
                                                                                                              PropertyDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(m.Value, this.settings)), NameHelper.GetSafeVariableName(m.Value.Name))
                                                                                                              .WithAccessorList(
                                                                                                                  SyntaxFactory.AccessorList(
                                                                                                                      SyntaxFactory.List <AccessorDeclarationSyntax>(new AccessorDeclarationSyntax[]
            {
                SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
                SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
            })))
                                                                                                              )));

            var nsContainer = NamespaceDeclaration(ParseName(this.settings.CsAstModelNamespace));

            nsContainer           = nsContainer.AddMembers(entityInterface);
            unit                  = unit.AddMembers(nsContainer);
            outputFile.SyntaxTree = unit.SyntaxTree;
        }
        private MemberDeclarationSyntax CreatePropertyDeclaration(TransportModelEntityMember member)
        {
            if (CsEmitterHelper.IsNode(member.Type))
            {
                if (member.Type.IsCollection)
                {
                    return(PropertyDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(member, this.settings)), NameHelper.GetSafeVariableName(member.Name))
                           .WithAccessorList(
                               SyntaxFactory.AccessorList(
                                   SyntaxFactory.List <AccessorDeclarationSyntax>(new AccessorDeclarationSyntax[]
                    {
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                        .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                        .WithModifiers(TokenList(CsEmitterHelper.IsNodeCollection(member.Type) ? new [] { Token(SyntaxKind.PrivateKeyword) } : new SyntaxToken[0]))
                        .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
                    })))
                           .AddModifiers(Token(SyntaxKind.PublicKeyword)));
                }
                else
                {
                    return(PropertyDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(member, this.settings)), NameHelper.GetSafeVariableName(member.Name))
                           .WithAccessorList(
                               SyntaxFactory.AccessorList(
                                   SyntaxFactory.List <AccessorDeclarationSyntax>(new AccessorDeclarationSyntax[]
                    {
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                        .WithBody(Block(List <StatementSyntax>(new [] { ReturnStatement(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(this.GetFieldName(member.Name)))) }))),
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                        .WithBody(
                            Block(
                                List <StatementSyntax>(new []
                        {
                            ExpressionStatement(
                                InvocationExpression(
                                    MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("SetAsParentFor")),
                                    ArgumentList(SeparatedList <ArgumentSyntax>(new []
                            {
                                Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(this.GetFieldName(member.Name)))),
                                Argument(IdentifierName("value"))
                            })))),
                            ExpressionStatement(
                                AssignmentExpression(
                                    SyntaxKind.SimpleAssignmentExpression,
                                    MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(this.GetFieldName(member.Name))),
                                    IdentifierName("value")))
                        })))
                    })))
                           .AddModifiers(Token(SyntaxKind.PublicKeyword)));
                }
            }
            else
            {
                if (member.Type.IsCollection)
                {
                    throw new InvalidOperationException("Non-node collections are not supported yet.");
                }

                return(PropertyDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(member, this.settings)), NameHelper.GetSafeVariableName(member.Name))
                       .WithAccessorList(
                           SyntaxFactory.AccessorList(
                               SyntaxFactory.List <AccessorDeclarationSyntax>(new AccessorDeclarationSyntax[]
                {
                    SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                    .WithBody(Block(List <StatementSyntax>(new [] { ReturnStatement(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(this.GetFieldName(member.Name)))) }))),
                    SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                    .WithBody(
                        Block(
                            List <StatementSyntax>(new []
                    {
                        ExpressionStatement(
                            InvocationExpression(
                                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("EnsureIsEditable")),
                                ArgumentList(SeparatedList <ArgumentSyntax>()))),
                        ExpressionStatement(
                            AssignmentExpression(
                                SyntaxKind.SimpleAssignmentExpression,
                                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(this.GetFieldName(member.Name))),
                                IdentifierName("value")))
                    })))
                })))
                       .AddModifiers(Token(SyntaxKind.PublicKeyword)));
            }
        }
        private void EmitEntity(TransportModelEntity entityModel, ICodeStream output)
        {
            var outputFile = (CodeFileCSharp)output.CreateCodeFile($"{CsEmitterHelper.GetCSharpModelShortName(entityModel, ModelType.Ast)}.cs");

            var unit = CompilationUnit();

            unit = unit.AddUsings(
                UsingDirective(ParseName(nameof(System))));

            var entityClass = ClassDeclaration(CsEmitterHelper.GetCSharpModelShortName(entityModel, ModelType.Ast));

            entityClass = entityClass.AddModifiers(Token(SyntaxKind.PublicKeyword));

            if (entityModel.TsDiscriminant == null || !(entityModel.TsDiscriminant is TransportModelEntityTsDiscriminantSyntaxKind))
            {
                entityClass = entityClass.AddModifiers(Token(SyntaxKind.AbstractKeyword));
            }

            List <StatementSyntax> propertyInitializers     = null;
            List <ArgumentSyntax>  baseConstructorArguments = null;

            var constructorDeclaration =
                ConstructorDeclaration(CsEmitterHelper.GetCSharpModelShortName(entityModel, ModelType.Ast))
                .AddModifiers(Token(SyntaxKind.PublicKeyword))
                .WithParameterList(ParameterList(SeparatedList <ParameterSyntax>(this.GetConstructorParameters(entityModel, out propertyInitializers, out baseConstructorArguments))));

            if (baseConstructorArguments != null && baseConstructorArguments.Count > 0)
            {
                constructorDeclaration = constructorDeclaration.WithInitializer(
                    ConstructorInitializer(SyntaxKind.BaseConstructorInitializer)
                    .AddArgumentListArguments(baseConstructorArguments.ToArray()));
            }

            var constructorBody = Block();

            constructorBody = this.PopulateRequiredConstructorBodyInitializers(constructorBody, entityModel);

            if (propertyInitializers != null && propertyInitializers.Count > 0)
            {
                constructorBody = constructorBody.AddStatements(propertyInitializers.ToArray());
            }

            constructorDeclaration = constructorDeclaration.WithBody(constructorBody);
            entityClass            = entityClass.AddMembers(constructorDeclaration);

            if (constructorDeclaration.ParameterList.Parameters.Count > 0)
            {
                var parameterlessConstructorDeclaration =
                    ConstructorDeclaration(CsEmitterHelper.GetCSharpModelShortName(entityModel, ModelType.Ast))
                    .AddModifiers(Token(SyntaxKind.PublicKeyword));
                var parameterlessConstructorBody = Block();
                parameterlessConstructorBody        = this.PopulateRequiredConstructorBodyInitializers(parameterlessConstructorBody, entityModel);
                parameterlessConstructorDeclaration = parameterlessConstructorDeclaration.WithBody(parameterlessConstructorBody);
                entityClass = entityClass.AddMembers(parameterlessConstructorDeclaration);
            }

            if (entityModel.GenericParameters.Count > 0)
            {
                entityClass = entityClass.WithTypeParameterList(TypeParameterList(SeparatedList <TypeParameterSyntax>(entityModel.GenericParameters.Select(p => TypeParameter(p.Key)))));

                entityClass = entityClass.WithConstraintClauses(List <TypeParameterConstraintClauseSyntax>(
                                                                    entityModel.GenericParameters.Select(p =>
                                                                                                         TypeParameterConstraintClause(
                                                                                                             IdentifierName(p.Key),
                                                                                                             SeparatedList <TypeParameterConstraintSyntax>(new[] { TypeConstraint(ParseTypeName(CsEmitterHelper.GetModelGenericParameterConstraintTypeName(p.Value, ModelType.Ast))) })))));
            }

            List <string> baseTypes = new List <string>();

            if (entityModel.Name == this.settings.AstNodeBaseTypeQualified.Split('.').Last())
            {
                baseTypes.Add($"{settings.CsAstModelNamespace}.{baseNodeType}");
            }

            if (entityModel.BaseEntity != null)
            {
                baseTypes.Add(CsEmitterHelper.GetCSharpModelReferenceName(entityModel.BaseEntity, this.settings, ModelType.Ast));
            }

            foreach (var interfaceModel in entityModel.Interfaces)
            {
                baseTypes.Add(CsEmitterHelper.GetCSharpModelFullyQualifiedName(interfaceModel, this.settings, ModelType.Ast));
            }

            if (baseTypes.Count > 0)
            {
                entityClass = entityClass.WithBaseList(BaseList(SeparatedList <BaseTypeSyntax>(baseTypes.Select(t => SimpleBaseType(ParseTypeName(t))))));
            }

            entityClass = entityClass.AddMembers(
                entityModel
                .Members
                .Where(m => !CsEmitterHelper.IsNodeCollection(m.Value.Type))
                .Select(m =>
                        FieldDeclaration(VariableDeclaration(ParseTypeName(CsEmitterHelper.GetAstModelPropertyTypeName(m.Value, this.settings)), SeparatedList <VariableDeclaratorSyntax>(new[] { VariableDeclarator(this.GetFieldName(m.Key)) })))
                        ).ToArray <MemberDeclarationSyntax>());

            entityClass = entityClass.AddMembers(entityModel.Members.Select(m => this.CreatePropertyDeclaration(m.Value)).ToArray <MemberDeclarationSyntax>());

            if (entityModel.TsDiscriminant is TransportModelEntityTsDiscriminantSyntaxKind)
            {
                entityClass = entityClass.AddMembers(this.GenerateConversionMethod(entityModel));
            }

            var nsContainer = NamespaceDeclaration(ParseName(this.settings.CsAstModelNamespace));

            nsContainer           = nsContainer.AddMembers(entityClass);
            unit                  = unit.AddMembers(nsContainer);
            outputFile.SyntaxTree = unit.SyntaxTree;
        }