public IXamlAstNode Transform()

in src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs [26:242]


        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (node is not XamlAstObjectNode on ||
                !context.GetAvaloniaTypes().Style.IsAssignableFrom(on.Type.GetClrType()))
                return node;

            var pn = on.Children.OfType<XamlAstXamlPropertyValueNode>()
                .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");

            // Missing selector, use the object's target type if available
            if (pn == null)
            {
                // We already went through this node
                if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode metadataNode
                    && metadataNode.Value == on)
                {
                    return node;
                }

                if (FindStyleParentObject(on, context) is { } parentObjectNode)
                {
                    return new AvaloniaXamlIlTargetTypeMetadataNode(
                        on,
                        new XamlAstClrTypeReference(node, parentObjectNode.Type.GetClrType(), false),
                        AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
                }

                return node;
            }

            if (pn.Values.Count != 1)
                throw new XamlSelectorsTransformException("Selector property should have exactly one value",
                    node);
            
            if (pn.Values[0] is XamlIlSelectorNode)
                //Deja vu. I've just been in this place before
                return node;
            
            if (!(pn.Values[0] is XamlAstTextNode tn))
                throw new XamlSelectorsTransformException("Selector property should be a text node", node);

            var selectorType = pn.Property.GetClrProperty().Getter!.ReturnType;
            var initialNode = new XamlIlSelectorInitialNode(node, selectorType);
            var avaloniaAttachedPropertyT = context.GetAvaloniaTypes().AvaloniaAttachedPropertyT;
            XamlIlSelectorNode Create(IEnumerable<SelectorGrammar.ISyntax> syntax,
                Func<string, string, XamlAstClrTypeReference> typeResolver)
            {
                XamlIlSelectorNode result = initialNode;
                XamlIlOrSelectorNode? results = null;
                foreach (var i in syntax)
                {
                    switch (i)
                    {

                        case SelectorGrammar.OfTypeSyntax ofType:
                            result = new XamlIlTypeSelector(result, typeResolver(ofType.Xmlns, ofType.TypeName).Type, true);
                            break;
                        case SelectorGrammar.IsSyntax @is:
                            result = new XamlIlTypeSelector(result, typeResolver(@is.Xmlns, @is.TypeName).Type, false);
                            break;
                        case SelectorGrammar.ClassSyntax @class:
                            result = new XamlIlStringSelector(result, XamlIlStringSelector.SelectorType.Class, @class.Class);
                            break;
                        case SelectorGrammar.NameSyntax name:
                            result = new XamlIlStringSelector(result, XamlIlStringSelector.SelectorType.Name, name.Name);
                            break;
                        case SelectorGrammar.PropertySyntax property:
                        {
                            var type = result.TargetType;

                            if (type == null)
                                throw new XamlTransformException("Property selectors must be applied to a type.", node);

                            var targetProperty =
                                type.GetAllProperties().FirstOrDefault(p => p.Name == property.Property);

                            if (targetProperty == null)
                                throw new XamlTransformException($"Cannot find '{property.Property}' on '{type}", node);

                            if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
                                new XamlAstTextNode(node, property.Value, type: context.Configuration.WellKnownTypes.String),
                                targetProperty, out var typedValue))
                                throw new XamlTransformException(
                                    $"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}",
                                    node);

                            result = new XamlIlPropertyEqualsSelector(result, targetProperty, typedValue);
                            break;
                        }
                        case SelectorGrammar.AttachedPropertySyntax attachedProperty:
                            {
                                var targetType = result.TargetType;
                                if (targetType == null)
                                {
                                    throw new XamlTransformException("Attached Property selectors must be applied to a type.",node);
                                }
                                var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type;

                                if (attachedPropertyOwnerType is null)
                                {
                                    throw new XamlTransformException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}",node);
                                }

                                var attachedPropertyName = attachedProperty.Property + "Property";

                                var targetPropertyField = attachedPropertyOwnerType.GetAllFields()
                                    .FirstOrDefault(f => f.IsStatic
                                        && f.IsPublic
                                        && f.Name == attachedPropertyName
                                        && f.FieldType.GenericTypeDefinition == avaloniaAttachedPropertyT
                                        );

                                if (targetPropertyField is null)
                                {
                                    throw new XamlTransformException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType.GetFqn()}", node);
                                }

                                var targetPropertyType = XamlIlAvaloniaPropertyHelper
                                    .GetAvaloniaPropertyType(targetPropertyField, context.GetAvaloniaTypes(), node);

                                if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
                                    new XamlAstTextNode(node, attachedProperty.Value, type: context.Configuration.WellKnownTypes.String),
                                    targetPropertyType, out var typedValue))
                                        throw new XamlTransformException(
                                            $"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}",
                                            node);

                                result = new XamlIlAttachedPropertyEqualsSelector(result, targetPropertyField, typedValue);
                                break;
                            }
                        case SelectorGrammar.ChildSyntax child:
                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Child);
                            break;
                        case SelectorGrammar.DescendantSyntax descendant:
                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Descendant);
                            break;
                        case SelectorGrammar.TemplateSyntax template:
                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Template);
                            break;
                        case SelectorGrammar.NotSyntax not:
                            result = new XamlIlNotSelector(result, Create(not.Argument, typeResolver));
                            break;
                        case SelectorGrammar.NthChildSyntax nth:
                            result = new XamlIlNthChildSelector(result, nth.Step, nth.Offset, XamlIlNthChildSelector.SelectorType.NthChild);
                            break;
                        case SelectorGrammar.NthLastChildSyntax nth:
                            result = new XamlIlNthChildSelector(result, nth.Step, nth.Offset, XamlIlNthChildSelector.SelectorType.NthLastChild);
                            break;
                        case SelectorGrammar.CommaSyntax comma:
                            if (results == null) 
                                results = new XamlIlOrSelectorNode(node, selectorType);
                            results.Add(result);
                            result = initialNode;
                            break;
                        case SelectorGrammar.NestingSyntax:
                            var parentTargetType = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();

                            if (parentTargetType is null)
                                throw new XamlTransformException($"Cannot find parent style for nested selector.", node);

                            result = new XamlIlNestingSelector(result, parentTargetType.TargetType.GetClrType());
                            break;
                        default:
                            throw new XamlTransformException($"Unsupported selector grammar '{i.GetType()}'.", node);
                    }
                }

                if (results != null)
                {
                    results.Add(result);
                }

                return results ?? result;
            }

            IEnumerable<SelectorGrammar.ISyntax> parsed;
            try
            {
                parsed = SelectorGrammar.Parse(tn.Text);
            }
            catch (Exception e)
            {
                throw new XamlSelectorsTransformException("Unable to parse selector: " + e.Message, node, e);
            }

            var selector = Create(parsed, (p, n) 
                => TypeReferenceResolver.ResolveType(context, $"{p}:{n}", true, node, true));
            pn.Values[0] = selector;

            var templateType = GetLastTemplateTypeFromSelector(selector);

            // Empty selector, use the object's target type if available
            if (selector == initialNode)
            {
                if (FindStyleParentObject(on, context) is { } parentObjectNode)
                {
                    return new AvaloniaXamlIlTargetTypeMetadataNode(
                        on,
                        new XamlAstClrTypeReference(node, parentObjectNode.Type.GetClrType(), false),
                        AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
                }

                return node;
            }
            
            var styleNode = new AvaloniaXamlIlTargetTypeMetadataNode(on,
                new XamlAstClrTypeReference(selector, selector.TargetType!, false),
                AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);

            return templateType switch
            {
                null => styleNode,
                _ => new AvaloniaXamlIlTargetTypeMetadataNode(styleNode,
                    new XamlAstClrTypeReference(styleNode, templateType, false),
                    AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate)
            };
        }