static ExpressionSyntax ClientProperty()

in src/tools/DevGenerators/CompositionGenerator/Generator.cs [45:305]


        static ExpressionSyntax ClientProperty(GClass c, GProperty p) =>
            MemberAccess(ServerName(c.Name), CompositionPropertyField(p));
        
        void GenerateClass(GClass cl)
        {
            var list = cl as GList;
            
            var unit = Unit();
            
            var clientNs = NamespaceDeclaration(IdentifierName("Avalonia.Rendering.Composition"));
            var serverNs = NamespaceDeclaration(IdentifierName("Avalonia.Rendering.Composition.Server"));
            var transportNs = NamespaceDeclaration(IdentifierName("Avalonia.Rendering.Composition.Transport"));

            var inherits = cl.Inherits ?? "CompositionObject";
            var abstractModifier = cl.Abstract ? new[] {SyntaxKind.AbstractKeyword} : null;
            var visibilityModifier = cl.Internal ? SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword;
            
            var client = ClassDeclaration(cl.Name)
                .AddModifiers(abstractModifier)
                .AddModifiers(visibilityModifier, SyntaxKind.UnsafeKeyword, SyntaxKind.PartialKeyword)
                .WithBaseType(inherits);
            
            var serverName = ServerName(cl.Name);
            var serverBase = cl.ServerBase ?? ServerName(cl.Inherits);
            if (list != null)
                serverBase = "ServerList<" + ServerName(list.ItemType) + ">";
            
            var server = ClassDeclaration(serverName)
                .AddModifiers(abstractModifier)
                .AddModifiers(SyntaxKind.UnsafeKeyword, SyntaxKind.PartialKeyword)
                .WithBaseType(serverBase);

            string changesName = ChangesName(cl.Name);
            string changedFieldsTypeName = ChangedFieldsTypeName(cl);
            string changedFieldsName = ChangedFieldsFieldName(cl);

            if (cl.Properties.Count > 0)
                client = client
                    .AddMembers(DeclareField(changedFieldsTypeName, changedFieldsName));
            
            
            if (!cl.CustomCtor)
            {
                client = client.AddMembers(PropertyDeclaration(ParseTypeName(serverName), "Server")
                    .AddModifiers(SyntaxKind.InternalKeyword, SyntaxKind.NewKeyword)
                    .AddAccessorListAccessors(AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                        .WithSemicolonToken(Semicolon())));
                client = client.AddMembers(
                    ConstructorDeclaration(cl.Name)
                        .AddModifiers(SyntaxKind.InternalKeyword)
                        .WithParameterList(ParameterList(SeparatedList(new[]
                        {
                            Parameter(Identifier("compositor")).WithType(ParseTypeName("Compositor")),
                            Parameter(Identifier("server")).WithType(ParseTypeName(serverName)),
                        })))
                        .WithInitializer(ConstructorInitializer(SyntaxKind.BaseConstructorInitializer,
                            ArgumentList(SeparatedList(new[]
                            {
                                Argument(IdentifierName("compositor")),
                                Argument(IdentifierName("server")),
                            })))).WithBody(Block(
                            ExpressionStatement(
                                AssignmentExpression(
                                    SyntaxKind.SimpleAssignmentExpression,
                                    IdentifierName("Server"),
                                    CastExpression(ParseTypeName(serverName), IdentifierName("server")))),
                            ExpressionStatement(InvocationExpression(IdentifierName("InitializeDefaults")))
                        )));
            }

            if (!cl.CustomServerCtor)
            {
                server = server.AddMembers(
                    ConstructorDeclaration(serverName)
                        .AddModifiers(SyntaxKind.InternalKeyword)
                        .WithParameterList(ParameterList(SeparatedList(new[]
                        {
                            Parameter(Identifier("compositor")).WithType(ParseTypeName("ServerCompositor")),
                        })))
                        .WithInitializer(ConstructorInitializer(SyntaxKind.BaseConstructorInitializer,
                            ArgumentList(SeparatedList(new[]
                            {
                                Argument(IdentifierName("compositor")),
                            })))).WithBody(Block(ParseStatement("Initialize();"))));
            }

            server = server.AddMembers(
                MethodDeclaration(ParseTypeName("void"), "Initialize")
                    .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));

            server = server.AddMembers(
                MethodDeclaration(ParseTypeName("void"), "DeserializeChangesExtra")
                    .AddParameterListParameters(Parameter(Identifier("c")).WithType(ParseTypeName("BatchStreamReader")))
                    .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));

            var resetBody = Block();
            var startAnimationBody = Block();
            var serverGetPropertyBody = Block();
            var serverGetCompositionPropertyBody = Block();
            var serializeMethodBody = SerializeChangesPrologue(cl);
            var deserializeMethodBody = DeserializeChangesPrologue(cl);

            var defaultsMethodBody = Block(ParseStatement("InitializeDefaultsExtra();"));
            
            foreach (var prop in cl.Properties)
            {
                var fieldName = PropertyBackingFieldName(prop);
                var typeInfo = GetTypeInfo(prop.Type);
                var (propType, filteredPropertyType, isObject, isNullable, isPassthrough,
                    serverPropertyType) = (typeInfo.RoslynType,
                    typeInfo.FilteredTypeName,
                    typeInfo.IsObject, typeInfo.IsNullable, typeInfo.IsPassthrough, typeInfo.ServerType);
                
                var animatedServer = prop.Animated;
                
                client = GenerateClientProperty(client, cl, prop, propType, isObject, isNullable);
                
                if (animatedServer)
                    server = server.AddMembers(
                        DeclareField(serverPropertyType, fieldName),
                        PropertyDeclaration(ParseTypeName(serverPropertyType), prop.Name)
                            .AddModifiers(SyntaxKind.PublicKeyword)
                            .AddAccessorListAccessors(
                                AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithExpressionBody(
                                    ArrowExpressionClause(IdentifierName(fieldName))).WithSemicolonToken(Semicolon()),
                                AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                                    .WithExpressionBody(ArrowExpressionClause(
                                            ParseExpression($"SetAnimatedValue({CompositionPropertyField(prop)}, out {PropertyBackingFieldName(prop)}, value)")))
                                    .WithSemicolonToken(Semicolon())));
                else
                {
                    server = server
                        .AddMembers(DeclareField(serverPropertyType, fieldName))
                        .AddMembers(PropertyDeclaration(ParseTypeName(serverPropertyType), prop.Name)
                            .AddModifiers(SyntaxKind.PublicKeyword)
                            .AddAccessorListAccessors(
                                AccessorDeclaration(SyntaxKind.GetAccessorDeclaration,
                                    Block(ReturnStatement(IdentifierName(fieldName)))),
                                AccessorDeclaration(SyntaxKind.SetAccessorDeclaration,
                                    Block(
                                        ParseStatement("var changed = false;"),
                                        IfStatement(BinaryExpression(SyntaxKind.NotEqualsExpression,
                                                 IdentifierName(fieldName),
                                                IdentifierName("value")),
                                            Block(
                                                ParseStatement("On" + prop.Name + "Changing();"),
                                                ParseStatement($"changed = true;"))
                                        ),
                                        ExpressionStatement(InvocationExpression(IdentifierName("SetValue"),
                                            ArgumentList(SeparatedList(new[]{
                                                    Argument(IdentifierName(CompositionPropertyField(prop))),
                                                    Argument(null, Token(SyntaxKind.RefKeyword), IdentifierName(fieldName)),
                                                    Argument(IdentifierName("value"))
                                                }
                                            )))),
                                        ParseStatement($"if(changed) On" + prop.Name + "Changed();")
                                    ))
                            ))
                        .AddMembers(MethodDeclaration(ParseTypeName("void"), "On" + prop.Name + "Changed")
                            .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()))
                        .AddMembers(MethodDeclaration(ParseTypeName("void"), "On" + prop.Name + "Changing")
                            .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));
                }
                
                resetBody = resetBody.AddStatements(
                    ExpressionStatement(InvocationExpression(MemberAccess(prop.Name, "Reset"))));
                
                serializeMethodBody = ApplySerializeField(serializeMethodBody, cl, prop, isObject, isPassthrough);
                deserializeMethodBody = ApplyDeserializeField(deserializeMethodBody,cl, prop, serverPropertyType, isObject);
                
                if (animatedServer)
                {
                    startAnimationBody = ApplyStartAnimation(startAnimationBody, cl, prop);
                }

                
                serverGetPropertyBody = ApplyGetProperty(serverGetPropertyBody, prop);
                serverGetCompositionPropertyBody = ApplyGetProperty(serverGetCompositionPropertyBody, prop, CompositionPropertyField(prop));


                string compositionPropertyVariantGetter = "null";
                if(VariantPropertyTypes.Contains(prop.Type))
                    compositionPropertyVariantGetter = $"obj => (({serverName})obj).{fieldName}";
                server = server.AddMembers(DeclareField($"CompositionProperty<{serverPropertyType}>", CompositionPropertyField(prop),
                    EqualsValueClause(ParseExpression(
                        $"CompositionProperty.Register<{serverName}, {serverPropertyType}>(\"{prop.Name}\", obj => (({serverName})obj).{fieldName}, (obj, v) => (({serverName})obj).{fieldName} = v, {compositionPropertyVariantGetter})")),
                    SyntaxKind.InternalKeyword, SyntaxKind.StaticKeyword));
                
                if (prop.DefaultValue != null)
                {
                    defaultsMethodBody = defaultsMethodBody.AddStatements(
                        ExpressionStatement(
                            AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
                                IdentifierName(prop.Name), ParseExpression(prop.DefaultValue))));
                }
            }
            
            if (cl.Properties.Count > 0)
            {
                server = server.AddMembers(((MethodDeclarationSyntax)ParseMemberDeclaration(
                            $"protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt){{}}")
                        !)
                    .WithBody(ApplyDeserializeChangesEpilogue(deserializeMethodBody, cl)));
                server = server.AddMembers(MethodDeclaration(ParseTypeName("void"), "OnFieldsDeserialized")
                    .WithParameterList(ParameterList(SingletonSeparatedList(Parameter(Identifier("changed"))
                        .WithType(ParseTypeName(ChangedFieldsTypeName(cl))))))
                    .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));
            }

            client = client.AddMembers(
                    MethodDeclaration(ParseTypeName("void"), "InitializeDefaults").WithBody(defaultsMethodBody))
                .AddMembers(
                    MethodDeclaration(ParseTypeName("void"), "InitializeDefaultsExtra")
                        .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));

            if (cl.Properties.Count > 0)
            {
                serializeMethodBody = serializeMethodBody.AddStatements(SerializeChangesEpilogue(cl));
                client = client.AddMembers(((MethodDeclarationSyntax)ParseMemberDeclaration(
                        $"private protected override void SerializeChangesCore(BatchStreamWriter writer){{}}")!)
                    .WithBody(serializeMethodBody));
            }

            if (list != null)
                client = AppendListProxy(list, client);

            if (startAnimationBody.Statements.Count != 0)
                client = WithStartAnimation(client, startAnimationBody);

            if (!cl.ServerOnly)
            {
                //server = WithGetPropertyForAnimation(server, serverGetPropertyBody);
                server = WithGetCompositionProperty(server, serverGetCompositionPropertyBody);
            }

            if (cl.ServerOnly)
                server = server.AddMembers(GenerateSerializeAllMethod(cl));
            
            if(cl.Implements.Count > 0)
                foreach (var impl in cl.Implements)
                {
                    client = client.WithBaseList(client.BaseList?.AddTypes(SimpleBaseType(ParseTypeName(impl.Name))));
                    if (impl.ServerName != null)
                        server = server.WithBaseList(
                            server.BaseList?.AddTypes(SimpleBaseType(ParseTypeName(impl.ServerName))));

                    if(ParseMemberDeclaration($"{impl.ServerName} {impl.Name}.Server => Server;") is { } member)
                        client = client.AddMembers(member);
                }


            SaveTo(unit.AddMembers(GenerateChangedFieldsEnum(cl)), "Transport",
                ChangedFieldsTypeName(cl) + ".generated.cs");

            if (!cl.ServerOnly)
                SaveTo(unit.AddMembers(clientNs.AddMembers(client)),
                    cl.Name + ".generated.cs");
            
            SaveTo(unit.AddMembers(serverNs.AddMembers(server)),
                "Server", "Server" + cl.Name + ".generated.cs");
        }