protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
 {
     if (!XamlIlAvaloniaPropertyHelper.Emit(context, codeGen, Property))
     {
         throw new XamlIlLoadException(
                   $"{Property.Name} of {(Property.Setter ?? Property.Getter).DeclaringType.GetFqn()} doesn't seem to be an AvaloniaProperty",
                   this);
     }
     context.Emit(Value, codeGen, context.Configuration.WellKnownTypes.Object);
     EmitCall(context, codeGen,
              m => m.Name == "PropertyEquals" &&
              m.Parameters.Count == 3 &&
              m.Parameters[1].FullName == "Avalonia.AvaloniaProperty" &&
              m.Parameters[2].Equals(context.Configuration.WellKnownTypes.Object));
 }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (!(node is XamlAstObjectNode on &&
                  on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
            {
                return(node);
            }

            var targetTypeNode = context.ParentNodes()
                                 .OfType <AvaloniaXamlIlTargetTypeMetadataNode>()
                                 .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) ??
                                 throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType", node);

            IXamlType propType = null;
            var       property = @on.Children.OfType <XamlAstXamlPropertyValueNode>()
                                 .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property");

            if (property != null)
            {
                var propertyName = property.Values.OfType <XamlAstTextNode>().FirstOrDefault()?.Text;
                if (propertyName == null)
                {
                    throw new XamlParseException("Setter.Property must be a string", node);
                }


                var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
                                                                                   new XamlAstClrTypeReference(targetTypeNode, targetTypeNode.TargetType.GetClrType(), false), property.Values[0]);
                property.Values = new List <IXamlAstValueNode> {
                    avaloniaPropertyNode
                };
                propType = avaloniaPropertyNode.AvaloniaPropertyType;
            }
            else
            {
                var propertyPath = on.Children.OfType <XamlAstXamlPropertyValueNode>()
                                   .FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath");
                if (propertyPath == null)
                {
                    throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node);
                }
                if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn &&
                    ppn.PropertyType != null)
                {
                    propType = ppn.PropertyType;
                }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (node is XamlAstXamlPropertyValueNode pv &&
                pv.Values.Count == 1 &&
                pv.Values[0] is XamlAstTextNode text &&
                pv.Property.GetClrProperty().Getter?.ReturnType
                .Equals(context.GetAvaloniaTypes().PropertyPath) == true
                )
            {
                var parentScope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>()
                                  .FirstOrDefault();
                if (parentScope == null)
                {
                    throw new XamlX.XamlParseException("No target type scope found for property path", text);
                }
                if (parentScope.ScopeType != AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style)
                {
                    throw new XamlX.XamlParseException("PropertyPath is currently only valid for styles", pv);
                }


                IEnumerable <PropertyPathGrammar.ISyntax> parsed;
                try
                {
                    parsed = PropertyPathGrammar.Parse(text.Text);
                }
                catch (Exception e)
                {
                    throw new XamlX.XamlParseException("Unable to parse PropertyPath: " + e.Message, text);
                }

                var       elements    = new List <IXamlIlPropertyPathElementNode>();
                IXamlType currentType = parentScope.TargetType.GetClrType();


                var expectProperty  = true;
                var expectCast      = true;
                var expectTraversal = false;
                var types           = context.GetAvaloniaTypes();

                IXamlType GetType(string ns, string name)
                {
                    return(TypeReferenceResolver.ResolveType(context, $"{ns}:{name}", false,
                                                             text, true).GetClrType());
                }

                void HandleProperty(string name, string typeNamespace, string typeName)
                {
                    if (!expectProperty || currentType == null)
                    {
                        throw new XamlX.XamlParseException("Unexpected property node", text);
                    }

                    var propertySearchType =
                        typeName != null?GetType(typeNamespace, typeName) : currentType;

                    IXamlIlPropertyPathElementNode prop = null;
                    var avaloniaPropertyFieldName       = name + "Property";
                    var avaloniaPropertyField           = propertySearchType.GetAllFields().FirstOrDefault(f =>
                                                                                                           f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldName);

                    if (avaloniaPropertyField != null)
                    {
                        prop = new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyField,
                                                                                 XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyField, types, text));
                    }
                    else
                    {
                        var clrProperty = propertySearchType.GetAllProperties().FirstOrDefault(p => p.Name == name);
                        prop = new XamlIClrPropertyPathElementNode(clrProperty);
                    }

                    if (prop == null)
                    {
                        throw new XamlX.XamlParseException(
                                  $"Unable to resolve property {name} on type {propertySearchType.GetFqn()}",
                                  text);
                    }

                    currentType = prop.Type;
                    elements.Add(prop);
                    expectProperty  = false;
                    expectTraversal = expectCast = true;
                }

                foreach (var ge in parsed)
                {
                    if (ge is PropertyPathGrammar.ChildTraversalSyntax)
                    {
                        if (!expectTraversal)
                        {
                            throw new XamlX.XamlParseException("Unexpected child traversal .", text);
                        }
                        elements.Add(new XamlIlChildTraversalPropertyPathElementNode());
                        expectTraversal = expectCast = false;
                        expectProperty  = true;
                    }
                    else if (ge is PropertyPathGrammar.EnsureTypeSyntax ets)
                    {
                        if (!expectCast)
                        {
                            throw new XamlX.XamlParseException("Unexpected cast node", text);
                        }
                        currentType = GetType(ets.TypeNamespace, ets.TypeName);
                        elements.Add(new XamlIlCastPropertyPathElementNode(currentType, true));
                        expectProperty = false;
                        expectCast     = expectTraversal = true;
                    }
                    else if (ge is PropertyPathGrammar.CastTypeSyntax cts)
                    {
                        if (!expectCast)
                        {
                            throw new XamlX.XamlParseException("Unexpected cast node", text);
                        }
                        //TODO: Check if cast can be done
                        currentType = GetType(cts.TypeNamespace, cts.TypeName);
                        elements.Add(new XamlIlCastPropertyPathElementNode(currentType, false));
                        expectProperty = false;
                        expectCast     = expectTraversal = true;
                    }
                    else if (ge is PropertyPathGrammar.PropertySyntax ps)
                    {
                        HandleProperty(ps.Name, null, null);
                    }
                    else if (ge is PropertyPathGrammar.TypeQualifiedPropertySyntax tqps)
                    {
                        HandleProperty(tqps.Name, tqps.TypeNamespace, tqps.TypeName);
                    }
                    else
                    {
                        throw new XamlX.XamlParseException("Unexpected node " + ge, text);
                    }
                }
                var propertyPathNode = new XamlIlPropertyPathNode(text, elements, types);
                if (propertyPathNode.Type == null)
                {
                    throw new XamlX.XamlParseException("Unexpected end of the property path", text);
                }
                pv.Values[0] = propertyPathNode;
            }

            return(node);
        }
        public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
        {
            if (!(node is XamlIlAstObjectNode on &&
                  on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
            {
                return(node);
            }

            var parent = context.ParentNodes().OfType <XamlIlAstObjectNode>()
                         .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style");

            if (parent == null)
            {
                throw new XamlIlParseException(
                          "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node);
            }
            var selectorProperty = parent.Children.OfType <XamlIlAstXamlPropertyValueNode>()
                                   .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");

            if (selectorProperty == null)
            {
                throw new XamlIlParseException(
                          "Can not find parent Style Selector", node);
            }
            var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode;

            if (selector?.TargetType == null)
            {
                throw new XamlIlParseException(
                          "Can not resolve parent Style Selector type", node);
            }


            var property = @on.Children.OfType <XamlIlAstXamlPropertyValueNode>()
                           .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property");

            if (property == null)
            {
                throw new XamlIlParseException("Setter without a property is not valid", node);
            }

            var propertyName = property.Values.OfType <XamlIlAstTextNode>().FirstOrDefault()?.Text;

            if (propertyName == null)
            {
                throw new XamlIlParseException("Setter.Property must be a string", node);
            }


            var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
                                                                               new XamlIlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]);

            property.Values = new List <IXamlIlAstValueNode>
            {
                avaloniaPropertyNode
            };

            var valueProperty = on.Children
                                .OfType <XamlIlAstXamlPropertyValueNode>().FirstOrDefault(p => p.Property.GetClrProperty().Name == "Value");

            if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlIlAstTextNode)
            {
                var propType = avaloniaPropertyNode.AvaloniaPropertyType;
                if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0],
                                                                      propType, out var converted))
                {
                    throw new XamlIlParseException(
                              $"Unable to convert property value to {propType.GetFqn()}",
                              valueProperty.Values[0]);
                }

                valueProperty.Property = new SetterValueProperty(valueProperty.Property,
                                                                 on.Type.GetClrType(), propType, context.GetAvaloniaTypes());
            }

            return(node);
        }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Style"))
            {
                return(node);
            }

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

            if (pn == null)
            {
                return(node);
            }

            if (pn.Values.Count != 1)
            {
                throw new XamlParseException("Selector property should 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 XamlParseException("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 XamlParseException("Property selectors must be applied to a type.", node);
                        }

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

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

                        if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
                                                                            new XamlAstTextNode(node, property.Value, type: context.Configuration.WellKnownTypes.String),
                                                                            targetProperty.PropertyType, out var typedValue))
                        {
                            throw new XamlParseException(
                                      $"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 XamlParseException("Attached Property selectors must be applied to a type.", node);
                        }
                        var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type;

                        if (attachedPropertyOwnerType is null)
                        {
                            throw new XamlParseException($"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 XamlParseException($"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 XamlParseException(
                                      $"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}",
                                      node);
                        }

                        result = new XamlIlAttacchedPropertyEqualsSelector(result, targetPropertyField, typedValue);
                        break;
                    }

                    case SelectorGrammar.ChildSyntax child:
                        result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Child);
                        break;

                    case SelectorGrammar.DescendantSyntax descendant:
                        result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Descendant);
                        break;

                    case SelectorGrammar.TemplateSyntax template:
                        result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.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;

                    default:
                        throw new XamlParseException($"Unsupported selector grammar '{i.GetType()}'.", node);
                    }
                }

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

                return(results ?? result);
            }

            IEnumerable <SelectorGrammar.ISyntax> parsed;

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

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

            pn.Values[0] = selector;

            return(new AvaloniaXamlIlTargetTypeMetadataNode(on,
                                                            new XamlAstClrTypeReference(selector, selector.TargetType, false),
                                                            AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style));
        }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (!(node is XamlAstObjectNode on &&
                  on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
            {
                return(node);
            }

            var parent = context.ParentNodes().OfType <XamlAstObjectNode>()
                         .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style");

            if (parent == null)
            {
                throw new XamlParseException(
                          "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node);
            }
            var selectorProperty = parent.Children.OfType <XamlAstXamlPropertyValueNode>()
                                   .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");

            if (selectorProperty == null)
            {
                throw new XamlParseException(
                          "Can not find parent Style Selector", node);
            }
            var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode;

            if (selector?.TargetType == null)
            {
                throw new XamlParseException(
                          "Can not resolve parent Style Selector type", node);
            }

            IXamlType propType = null;
            var       property = @on.Children.OfType <XamlAstXamlPropertyValueNode>()
                                 .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property");

            if (property != null)
            {
                var propertyName = property.Values.OfType <XamlAstTextNode>().FirstOrDefault()?.Text;
                if (propertyName == null)
                {
                    throw new XamlParseException("Setter.Property must be a string", node);
                }


                var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
                                                                                   new XamlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]);
                property.Values = new List <IXamlAstValueNode> {
                    avaloniaPropertyNode
                };
                propType = avaloniaPropertyNode.AvaloniaPropertyType;
            }
            else
            {
                var propertyPath = on.Children.OfType <XamlAstXamlPropertyValueNode>()
                                   .FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath");
                if (propertyPath == null)
                {
                    throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node);
                }
                if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn &&
                    ppn.PropertyType != null)
                {
                    propType = ppn.PropertyType;
                }