public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
            {
                if (node is XamlPropertyAssignmentNode pa)
                {
                    if (pa.Property.Name == "Name" &&
                        pa.Property.DeclaringType.FullName == "Robust.Client.UserInterface.Control")
                    {
                        if (context.ParentNodes().FirstOrDefault() is XamlManipulationGroupNode mg &&
                            mg.Children.OfType <RobustNameScopeRegistrationXamlIlNode>().Any())
                        {
                            return(node);
                        }

                        IXamlAstValueNode value = null;
                        for (var c = 0; c < pa.Values.Count; c++)
                        {
                            if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
                            {
                                value = pa.Values[c];
                                if (!(value is XamlAstTextNode))
                                {
                                    var local = new XamlAstCompilerLocalNode(value);
                                    // Wrap original in local initialization
                                    pa.Values[c] = new XamlAstLocalInitializationNodeEmitter(value, value, local);
                                    // Use local
                                    value = local;
                                }

                                break;
                            }
                        }

                        if (value != null)
                        {
                            var objectType = context.ParentNodes().OfType <XamlAstConstructableObjectNode>().FirstOrDefault()?.Type.GetClrType();
                            return(new XamlManipulationGroupNode(pa)
                            {
                                Children =
                                {
                                    pa,
                                    new RobustNameScopeRegistrationXamlIlNode(value, objectType)
                                }
                            });
                        }
                    }

                    /*else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any())
                     * {
                     *  pa.Values[pa.Values.Count - 1] =
                     *      new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]);
                     * }*/
                }

                return(node);
            }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlCompileBindingsNode)
            {
                return(node);
            }

            if (node is XamlAstObjectNode obj)
            {
                foreach (var item in obj.Children)
                {
                    if (item is XamlAstXmlDirective directive)
                    {
                        if (directive.Namespace == XamlNamespaces.Xaml2006 &&
                            directive.Name == "CompileBindings" &&
                            directive.Values.Count == 1)
                        {
                            if (!(directive.Values[0] is XamlAstTextNode text &&
                                  bool.TryParse(text.Text, out var compileBindings)))
                            {
                                throw new XamlParseException("The value of x:CompileBindings must be a literal boolean value.", directive.Values[0]);
                            }

                            obj.Children.Remove(directive);

                            return(new AvaloniaXamlIlCompileBindingsNode(obj, compileBindings));
                        }
                    }
                }
            }

            // Convert the <Binding> tag to either a CompiledBinding or ReflectionBinding tag.

            if (node is XamlAstXmlTypeReference tref &&
                tref.Name == "Binding" &&
                tref.XmlNamespace == "https://github.com/avaloniaui")
            {
                tref.IsMarkupExtension = true;

                var compileBindings = context.ParentNodes()
                                      .OfType <AvaloniaXamlIlCompileBindingsNode>()
                                      .FirstOrDefault()
                                      ?.CompileBindings ?? CompileBindingsByDefault;

                tref.Name = compileBindings ? "CompiledBinding" : "ReflectionBinding";
            }
            return(node);
        }
Exemple #3
0
        public static bool CustomValueConverter(AstTransformationContext context,
                                                IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
        {
            if (type.FullName == "System.TimeSpan" &&
                node is XamlAstTextNode tn &&
                !tn.Text.Contains(":"))
            {
                var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
                result = new XamlStaticOrTargetedReturnMethodCallNode(tn,
                                                                      type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
                                                                      new[]
                {
                    new XamlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
                });
                return(true);
            }

            if (type.FullName == "Avalonia.AvaloniaProperty")
            {
                var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
                if (scope == null)
                {
                    throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
                }
                if (!(node is XamlAstTextNode text))
                {
                    throw new XamlX.XamlLoadException("Property should be a text node", node);
                }
                result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
                return(true);
            }

            result = null;
            return(false);
        }
Exemple #4
0
        private static XamlIlBindingPathNode TransformForTargetTyping(XamlIlBindingPathNode transformed, AstTransformationContext context)
        {
            var parentNode = context.ParentNodes().OfType <XamlPropertyAssignmentNode>().FirstOrDefault();

            if (parentNode == null)
            {
                return(transformed);
            }

            var lastElement =
                transformed.Elements[transformed.Elements.Count - 1];

            if (parentNode.Property?.Getter?.ReturnType == context.GetAvaloniaTypes().ICommand&& lastElement is XamlIlClrMethodPathElementNode methodPathElement)
            {
                IXamlMethod   executeMethod       = methodPathElement.Method;
                IXamlMethod   canExecuteMethod    = executeMethod.DeclaringType.FindMethod(new FindMethodMethodSignature($"Can{executeMethod.Name}", context.Configuration.WellKnownTypes.Boolean, context.Configuration.WellKnownTypes.Object));
                List <string> dependsOnProperties = new();
                if (canExecuteMethod is not null)
                {
                    foreach (var attr in canExecuteMethod.CustomAttributes)
                    {
                        if (attr.Type == context.GetAvaloniaTypes().DependsOnAttribute)
                        {
                            dependsOnProperties.Add((string)attr.Parameters[0]);
                        }
                    }
                }
                transformed.Elements.RemoveAt(transformed.Elements.Count - 1);
                transformed.Elements.Add(new XamlIlClrMethodAsCommandPathElementNode(context.GetAvaloniaTypes().ICommand, executeMethod, canExecuteMethod, dependsOnProperties));
            }

            return(transformed);
        }
Exemple #5
0
        public static bool CustomValueConverter(AstTransformationContext context,
                                                IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
        {
            if (!(node is XamlAstTextNode textNode))
            {
                result = null;
                return(false);
            }

            var text = textNode.Text;

            var types = context.GetAvaloniaTypes();

            if (type.FullName == "System.TimeSpan")
            {
                var tsText = text.Trim();

                if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
                {
                    // // shorthand seconds format (ie. "0.25")
                    if (!tsText.Contains(":") && double.TryParse(tsText,
                                                                 NumberStyles.Float | NumberStyles.AllowThousands,
                                                                 CultureInfo.InvariantCulture, out var seconds))
                    {
                        timeSpan = TimeSpan.FromSeconds(seconds);
                    }
                    else
                    {
                        throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
                    }
                }


                result = new XamlStaticOrTargetedReturnMethodCallNode(node,
                                                                      type.FindMethod("FromTicks", type, false, types.Long),
                                                                      new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
                return(true);
            }

            if (type.Equals(types.FontFamily))
            {
                result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
                return(true);
            }

            if (type.FullName == "Avalonia.AvaloniaProperty")
            {
                var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
                if (scope == null)
                {
                    throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
                }

                result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node);
                return(true);
            }

            result = null;
            return(false);
        }
        public static bool CustomValueConverter(AstTransformationContext context,
                                                IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
        {
            if (!(node is XamlAstTextNode textNode))
            {
                result = null;
                return(false);
            }

            var text  = textNode.Text;
            var types = context.GetAvaloniaTypes();

            if (AvaloniaXamlIlLanguageParseIntrinsics.TryConvert(context, node, text, type, types, out result))
            {
                return(true);
            }

            if (type.FullName == "Avalonia.AvaloniaProperty")
            {
                var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
                if (scope == null)
                {
                    throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
                }

                result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node);
                return(true);
            }

            result = null;
            return(false);
        }
 public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
 {
     if (!context.ParentNodes().Any() &&
         node is XamlValueWithManipulationNode mnode)
     {
         mnode.Manipulation = new XamlManipulationGroupNode(mnode,
                                                            new[]
         {
             mnode.Manipulation,
             new HandleRootObjectScopeNode(mnode)
         });
     }
     return(node);
 }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (node is IXamlAstValueNode vn)
            {
                if (context.ParentNodes().FirstOrDefault() is XamlMarkupExtensionNode)
                {
                    return(node);
                }

                if (XamlTransformHelpers.TryConvertMarkupExtension(context, vn, out var rv))
                {
                    return(rv);
                }
            }

            return(node);
        }
        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 XamlAstConstructableObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
            {
                IXamlType startType;
                var       parentDataContextNode = context.ParentNodes().OfType <AvaloniaXamlIlDataContextTypeMetadataNode>().FirstOrDefault();
                if (parentDataContextNode is null)
                {
                    throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding);
                }

                startType = parentDataContextNode.DataContextType;

                XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, binding, startType);
            }

            return(node);
        }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            var bindingPriorityType = context.GetAvaloniaTypes().BindingPriority;

            // The node is a candidate for transformation if:
            // - It's a property assignment to an Avalonia property
            // - There's a ControlTemplate ancestor
            // - The property has a single value
            if (node is XamlPropertyAssignmentNode prop &&
                prop.Property is XamlIlAvaloniaProperty avaloniaProperty &&
                context.ParentNodes().Any(IsControlTemplate) &&
                prop.Values.Count == 1)
            {
                var priorityValueSetters = new List <IXamlPropertySetter>();

                // Iterate through the possible setters, trying to find a setter on the property
                // which has a BindingPriority parameter followed by the parameter of the existing
                // setter.
                foreach (var setter in prop.PossibleSetters)
                {
                    var s = avaloniaProperty.Setters.FirstOrDefault(x =>
                                                                    x.Parameters[0] == bindingPriorityType &&
                                                                    x.Parameters[1] == setter.Parameters[0]);
                    if (s != null)
                    {
                        priorityValueSetters.Add(s);
                    }
                }

                // If any BindingPriority setters were found, use those.
                if (priorityValueSetters.Count > 0)
                {
                    prop.PossibleSetters = priorityValueSetters;
                    prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.TemplatedParent));
                }
            }

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

            // Check if we've already transformed this node.
            if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode)
            {
                return(node);
            }

            var targetTypeNode = on.Children.OfType <XamlAstXamlPropertyValueNode>()
                                 .FirstOrDefault(p => p.Property.GetClrProperty().Name == "TargetType") ??
                                 throw new XamlParseException("ControlTheme must have a TargetType.", node);

            IXamlType targetType;

            if (targetTypeNode.Values[0] is XamlTypeExtensionNode extension)
            {
                targetType = extension.Value.GetClrType();
            }
            else if (targetTypeNode.Values[0] is XamlAstTextNode text)
            {
                targetType = TypeReferenceResolver.ResolveType(context, text.Text, false, text, true).GetClrType();
            }
            else
            {
                throw new XamlParseException("Could not determine TargetType for ControlTheme.", targetTypeNode);
            }

            return(new AvaloniaXamlIlTargetTypeMetadataNode(on,
                                                            new XamlAstClrTypeReference(targetTypeNode, targetType, false),
                                                            AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style));
        }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (!(node is XamlAstObjectNode on &&
                  on.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.Templates.ControlTemplate"))
            {
                return(node);
            }
            var tt = on.Children.OfType <XamlAstXamlPropertyValueNode>().FirstOrDefault(ch =>
                                                                                        ch.Property.GetClrProperty().Name == "TargetType");

            if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode)
            {
                // Deja vu. I've just been in this place before
                return(node);
            }

            IXamlAstTypeReference targetType;

            var templatableBaseType = context.Configuration.TypeSystem.GetType("Avalonia.Controls.Control");

            if ((tt?.Values.FirstOrDefault() is XamlTypeExtensionNode tn))
            {
                targetType = tn.Value;
            }
        private static BindingExpressionGrammar.INode ConvertLongFormPropertiesToBindingExpressionNode(
            AstTransformationContext context,
            XamlAstObjectNode binding)
        {
            BindingExpressionGrammar.INode convertedNode = null;

            var syntheticCompiledBindingProperties = binding.Children.OfType <XamlAstXamlPropertyValueNode>()
                                                     .Where(v => v.Property is AvaloniaSyntheticCompiledBindingProperty)
                                                     .ToList();

            var elementNameProperty = syntheticCompiledBindingProperties
                                      .FirstOrDefault(v =>
                                                      v.Property is AvaloniaSyntheticCompiledBindingProperty prop &&
                                                      prop.Name == SyntheticCompiledBindingPropertyName.ElementName);

            var sourceProperty = syntheticCompiledBindingProperties
                                 .FirstOrDefault(v =>
                                                 v.Property is AvaloniaSyntheticCompiledBindingProperty prop &&
                                                 prop.Name == SyntheticCompiledBindingPropertyName.Source);

            var relativeSourceProperty = syntheticCompiledBindingProperties
                                         .FirstOrDefault(v =>
                                                         v.Property is AvaloniaSyntheticCompiledBindingProperty prop &&
                                                         prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource);

            if (elementNameProperty?.Values[0] is XamlAstTextNode elementName)
            {
                convertedNode = new BindingExpressionGrammar.NameNode {
                    Name = elementName.Text
                };
            }
            else if (elementNameProperty != null)
            {
                throw new XamlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
            }

            if (sourceProperty?.Values[0] != null)
            {
                if (convertedNode != null)
                {
                    throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
                }

                convertedNode = new RawSourceBindingExpressionNode(sourceProperty?.Values[0]);
            }

            if (GetRelativeSourceObjectFromAssignment(
                    context,
                    relativeSourceProperty,
                    out var relativeSourceObject))
            {
                if (convertedNode != null)
                {
                    throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
                }

                var mode = relativeSourceObject.Children
                           .OfType <XamlAstXamlPropertyValueNode>()
                           .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Mode")
                           ?.Values[0] is XamlAstTextNode modeAssignedValue ? modeAssignedValue.Text : null;
                if (relativeSourceObject.Arguments.Count == 0 && mode == null)
                {
                    mode = "FindAncestor";
                }

                if (mode == "FindAncestor")
                {
                    var ancestorLevel = relativeSourceObject.Children
                                        .OfType <XamlAstXamlPropertyValueNode>()
                                        .FirstOrDefault(x => x.Property.GetClrProperty().Name == "FindAncestor")
                                        ?.Values[0] is XamlAstTextNode ancestorLevelText?int.Parse(ancestorLevelText.Text) - 1 : 0;

                    var treeType = relativeSourceObject.Children
                                   .OfType <XamlAstXamlPropertyValueNode>()
                                   .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Tree")
                                   ?.Values[0] is XamlAstTextNode treeTypeValue ? treeTypeValue.Text : "Visual";

                    var ancestorTypeName = relativeSourceObject.Children
                                           .OfType <XamlAstXamlPropertyValueNode>()
                                           .FirstOrDefault(x => x.Property.GetClrProperty().Name == "AncestorType")
                                           ?.Values[0] as XamlAstTextNode;

                    IXamlType ancestorType = null;
                    if (ancestorTypeName is null)
                    {
                        if (treeType == "Visual")
                        {
                            throw new XamlParseException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.", relativeSourceObject);
                        }
                        else if (treeType == "Logical")
                        {
                            var styledElementType = context.GetAvaloniaTypes().StyledElement;
                            ancestorType = context
                                           .ParentNodes()
                                           .OfType <XamlAstObjectNode>()
                                           .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType()))
                                           .ElementAtOrDefault(ancestorLevel)
                                           ?.Type.GetClrType();

                            if (ancestorType is null)
                            {
                                throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", relativeSourceObject);
                            }
                        }
                    }
                    else
                    {
                        ancestorType = TypeReferenceResolver.ResolveType(
                            context,
                            ancestorTypeName.Text,
                            false,
                            ancestorTypeName,
                            true).GetClrType();
                    }

                    if (treeType == "Visual")
                    {
                        convertedNode = new VisualAncestorBindingExpressionNode
                        {
                            Type  = ancestorType,
                            Level = ancestorLevel
                        };
                    }
                    else if (treeType == "Logical")
                    {
                        convertedNode = new LogicalAncestorBindingExpressionNode
                        {
                            Type  = ancestorType,
                            Level = ancestorLevel
                        };
                    }
                    else
                    {
                        throw new XamlParseException($"Unknown tree type '{treeType}'.", binding);
                    }
                }
                else if (mode == "DataContext")
                {
                    convertedNode = null;
                }
                else if (mode == "Self")
                {
                    convertedNode = new BindingExpressionGrammar.SelfNode();
                }
                else if (mode == "TemplatedParent")
                {
                    var parentType = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>()
                                     .FirstOrDefault(x =>
                                                     x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate)
                                     ?.TargetType.GetClrType();

                    if (parentType is null)
                    {
                        throw new XamlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding);
                    }

                    convertedNode = new TemplatedParentBindingExpressionNode {
                        Type = parentType
                    };
                }
                else
                {
                    throw new XamlParseException($"Unknown RelativeSource mode '{mode}'.", binding);
                }
            }

            if (elementNameProperty != null)
            {
                binding.Children.Remove(elementNameProperty);
            }
            if (sourceProperty != null)
            {
                binding.Children.Remove(sourceProperty);
            }
            if (relativeSourceProperty != null)
            {
                binding.Children.Remove(relativeSourceProperty);
            }

            return(convertedNode);
        }
Exemple #15
0
        public static bool CustomValueConverter(AstTransformationContext context,
                                                IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
        {
            if (!(node is XamlAstTextNode textNode))
            {
                result = null;
                return(false);
            }

            var text = textNode.Text;

            var types = context.GetAvaloniaTypes();

            if (type.FullName == "System.TimeSpan")
            {
                var tsText = text.Trim();

                if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
                {
                    // // shorthand seconds format (ie. "0.25")
                    if (!tsText.Contains(":") && double.TryParse(tsText,
                                                                 NumberStyles.Float | NumberStyles.AllowThousands,
                                                                 CultureInfo.InvariantCulture, out var seconds))
                    {
                        timeSpan = TimeSpan.FromSeconds(seconds);
                    }
                    else
                    {
                        throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
                    }
                }


                result = new XamlStaticOrTargetedReturnMethodCallNode(node,
                                                                      type.FindMethod("FromTicks", type, false, types.Long),
                                                                      new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
                return(true);
            }

            if (type.Equals(types.FontFamily))
            {
                result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
                return(true);
            }

            if (type.Equals(types.Thickness))
            {
                var thickness = Thickness.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
                                                                     new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });

                return(true);
            }

            if (type.Equals(types.Point))
            {
                var point = Point.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
                                                                     new[] { point.X, point.Y });

                return(true);
            }

            if (type.Equals(types.Vector))
            {
                var vector = Vector.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
                                                                     new[] { vector.X, vector.Y });

                return(true);
            }

            if (type.Equals(types.Size))
            {
                var size = Size.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
                                                                     new[] { size.Width, size.Height });

                return(true);
            }

            if (type.Equals(types.Matrix))
            {
                var matrix = Matrix.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
                                                                     new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });

                return(true);
            }

            if (type.Equals(types.CornerRadius))
            {
                var cornerRadius = CornerRadius.Parse(text);

                result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
                                                                     new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });

                return(true);
            }

            if (type.FullName == "Avalonia.AvaloniaProperty")
            {
                var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
                if (scope == null)
                {
                    throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
                }

                result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node);
                return(true);
            }

            result = null;
            return(false);
        }
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlDataContextTypeMetadataNode)
            {
                // We've already resolved the data context type for this node.
                return(node);
            }

            if (node is XamlAstConstructableObjectNode on)
            {
                AvaloniaXamlIlDataContextTypeMetadataNode inferredDataContextTypeNode  = null;
                AvaloniaXamlIlDataContextTypeMetadataNode directiveDataContextTypeNode = null;
                bool isDataTemplate = on.Type.GetClrType().Equals(context.GetAvaloniaTypes().DataTemplate);

                for (int i = 0; i < on.Children.Count; ++i)
                {
                    var child = on.Children[i];
                    if (child is XamlAstXmlDirective directive)
                    {
                        if (directive.Namespace == XamlNamespaces.Xaml2006 &&
                            directive.Name == "DataType" &&
                            directive.Values.Count == 1)
                        {
                            on.Children.RemoveAt(i);
                            i--;
                            if (directive.Values[0] is XamlAstTextNode text)
                            {
                                directiveDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on,
                                                                                                             TypeReferenceResolver.ResolveType(context, text.Text, isMarkupExtension: false, text, strict: true).Type);
                            }
                            else
                            {
                                throw new XamlX.XamlParseException("x:DataType should be set to a type name.", directive.Values[0]);
                            }
                        }
                    }
                    else if (child is XamlPropertyAssignmentNode pa)
                    {
                        if (pa.Property.Name == "DataContext" &&
                            pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) &&
                            pa.Values[0] is XamlMarkupExtensionNode ext &&
                            ext.Value is XamlAstConstructableObjectNode obj)
                        {
                            inferredDataContextTypeNode = ParseDataContext(context, on, obj);
                        }
                        else if (isDataTemplate &&
                                 pa.Property.Name == "DataType" &&
                                 pa.Values[0] is XamlTypeExtensionNode dataTypeNode)
                        {
                            inferredDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, dataTypeNode.Value.GetClrType());
                        }
                    }
                }

                // If there is no x:DataType directive,
                // do more specialized inference
                if (directiveDataContextTypeNode is null)
                {
                    if (isDataTemplate && inferredDataContextTypeNode is null)
                    {
                        // Infer data type from collection binding on a control that displays items.
                        var parentObject = context.ParentNodes().OfType <XamlAstConstructableObjectNode>().FirstOrDefault();
                        if (parentObject != null && context.GetAvaloniaTypes().IItemsPresenterHost.IsDirectlyAssignableFrom(parentObject.Type.GetClrType()))
                        {
                            inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject);
                        }
                        else
                        {
                            inferredDataContextTypeNode = new AvaloniaXamlIlUninferrableDataContextMetadataNode(on);
                        }
                    }
                }

                return(directiveDataContextTypeNode ?? inferredDataContextTypeNode ?? node);
            }
        private static IXamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, IXamlType startType, IEnumerable <BindingExpressionGrammar.INode> bindingExpression)
        {
            List <IXamlIlBindingPathElementNode> transformNodes = new List <IXamlIlBindingPathElementNode>();
            List <IXamlIlBindingPathElementNode> nodes          = new List <IXamlIlBindingPathElementNode>();

            foreach (var astNode in bindingExpression)
            {
                var targetType = nodes.Count == 0 ? startType : nodes[nodes.Count - 1].Type;
                switch (astNode)
                {
                case BindingExpressionGrammar.EmptyExpressionNode _:
                    break;

                case BindingExpressionGrammar.NotNode _:
                    transformNodes.Add(new XamlIlNotPathElementNode(context.Configuration.WellKnownTypes.Boolean));
                    break;

                case BindingExpressionGrammar.StreamNode _:
                    IXamlType observableType;
                    if (targetType.GenericTypeDefinition?.Equals(context.Configuration.TypeSystem.FindType("System.IObservable`1")) == true)
                    {
                        observableType = targetType;
                    }
                    else
                    {
                        observableType = targetType.GetAllInterfaces().FirstOrDefault(i => i.GenericTypeDefinition?.Equals(context.Configuration.TypeSystem.FindType("System.IObservable`1")) ?? false);
                    }

                    if (observableType != null)
                    {
                        nodes.Add(new XamlIlStreamObservablePathElementNode(observableType.GenericArguments[0]));
                        break;
                    }
                    bool foundTask = false;
                    for (var currentType = targetType; currentType != null; currentType = currentType.BaseType)
                    {
                        if (currentType.GenericTypeDefinition.Equals(context.Configuration.TypeSystem.GetType("System.Threading.Tasks.Task`1")))
                        {
                            foundTask = true;
                            nodes.Add(new XamlIlStreamTaskPathElementNode(currentType.GenericArguments[0]));
                            break;
                        }
                    }
                    if (foundTask)
                    {
                        break;
                    }
                    throw new XamlX.XamlParseException($"Compiled bindings do not support stream bindings for objects of type {targetType.FullName}.", lineInfo);

                case BindingExpressionGrammar.PropertyNameNode propName:
                    var avaloniaPropertyFieldNameMaybe = propName.PropertyName + "Property";
                    var avaloniaPropertyFieldMaybe     = targetType.GetAllFields().FirstOrDefault(f =>
                                                                                                  f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldNameMaybe);

                    if (avaloniaPropertyFieldMaybe != null)
                    {
                        nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyFieldMaybe,
                                                                                    XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyFieldMaybe, context.GetAvaloniaTypes(), lineInfo)));
                    }
                    else
                    {
                        var clrProperty = targetType.GetAllProperties().FirstOrDefault(p => p.Name == propName.PropertyName);

                        if (clrProperty is null)
                        {
                            throw new XamlX.XamlParseException($"Unable to resolve property of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
                        }
                        nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty));
                    }
                    break;

                case BindingExpressionGrammar.IndexerNode indexer:
                {
                    if (targetType.IsArray)
                    {
                        nodes.Add(new XamlIlArrayIndexerPathElementNode(targetType, indexer.Arguments, lineInfo));
                        break;
                    }

                    IXamlProperty property = null;
                    for (var currentType = targetType; currentType != null; currentType = currentType.BaseType)
                    {
                        var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute");
                        if (defaultMemberAttribute != null)
                        {
                            property = targetType.GetAllProperties().FirstOrDefault(x => x.Name == (string)defaultMemberAttribute.Parameters[0]);
                            break;
                        }
                    }
                    ;
                    if (property is null)
                    {
                        throw new XamlX.XamlParseException($"The type '${targetType}' does not have an indexer.", lineInfo);
                    }

                    IEnumerable <IXamlType> parameters = property.IndexerParameters;

                    List <IXamlAstValueNode> values = new List <IXamlAstValueNode>();
                    int currentParamIndex           = 0;
                    foreach (var param in parameters)
                    {
                        var textNode = new XamlAstTextNode(lineInfo, indexer.Arguments[currentParamIndex], type: context.Configuration.WellKnownTypes.String);
                        if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, textNode,
                                                                            param, out var converted))
                        {
                            throw new XamlX.XamlParseException(
                                      $"Unable to convert indexer parameter value of '{indexer.Arguments[currentParamIndex]}' to {param.GetFqn()}",
                                      textNode);
                        }

                        values.Add(converted);
                        currentParamIndex++;
                    }

                    bool isNotifyingCollection = targetType.GetAllInterfaces().Any(i => i.FullName == "System.Collections.Specialized.INotifyCollectionChanged");

                    nodes.Add(new XamlIlClrIndexerPathElementNode(property, values, string.Join(",", indexer.Arguments), isNotifyingCollection));
                    break;
                }

                case BindingExpressionGrammar.AttachedPropertyNameNode attachedProp:
                    var avaloniaPropertyFieldName = attachedProp.PropertyName + "Property";
                    var avaloniaPropertyField     = GetType(attachedProp.Namespace, attachedProp.TypeName).GetAllFields().FirstOrDefault(f =>
                                                                                                                                         f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldName);
                    nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyField,
                                                                                XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyField, context.GetAvaloniaTypes(), lineInfo)));
                    break;

                case BindingExpressionGrammar.SelfNode _:
                    nodes.Add(new SelfPathElementNode(targetType));
                    break;

                case VisualAncestorBindingExpressionNode visualAncestor:
                    nodes.Add(new FindVisualAncestorPathElementNode(visualAncestor.Type, visualAncestor.Level));
                    break;

                case TemplatedParentBindingExpressionNode templatedParent:
                    var templatedParentField = context.GetAvaloniaTypes().StyledElement.GetAllFields()
                                               .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == "TemplatedParentProperty");
                    nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(
                                  templatedParentField,
                                  templatedParent.Type));
                    break;

                case BindingExpressionGrammar.AncestorNode ancestor:
                    if (ancestor.Namespace is null && ancestor.TypeName is null)
                    {
                        var styledElementType = context.GetAvaloniaTypes().StyledElement;
                        var ancestorType      = context
                                                .ParentNodes()
                                                .OfType <XamlAstConstructableObjectNode>()
                                                .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType()))
                                                .ElementAtOrDefault(ancestor.Level)
                                                ?.Type.GetClrType();

                        if (ancestorType is null)
                        {
                            throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", lineInfo);
                        }

                        nodes.Add(new FindAncestorPathElementNode(ancestorType, ancestor.Level));
                    }
                    else
                    {
                        nodes.Add(new FindAncestorPathElementNode(GetType(ancestor.Namespace, ancestor.TypeName), ancestor.Level));
                    }
                    break;
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (node is XamlAstConstructableObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
            {
                IXamlType startType        = null;
                var       sourceProperty   = binding.Children.OfType <XamlPropertyAssignmentNode>().FirstOrDefault(c => c.Property.Name == "Source");
                var       dataTypeProperty = binding.Children.OfType <XamlPropertyAssignmentNode>().FirstOrDefault(c => c.Property.Name == "DataType");
                if (sourceProperty?.Values.Count is 1)
                {
                    var sourceValue = sourceProperty.Values[0];
                    switch (sourceValue)
                    {
                    case XamlAstTextNode textNode:
                        startType = textNode.Type?.GetClrType();
                        break;

                    case XamlMarkupExtensionNode extension:
                        startType = extension.Type?.GetClrType();

                        //let's try to infer StaticResource type from parent resources in xaml
                        if (extension.Value.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.MarkupExtensions.StaticResourceExtension" &&
                            extension.Value is XamlAstConstructableObjectNode cn &&
                            cn.Arguments.Count == 1 && cn.Arguments[0] is XamlAstTextNode keyNode)
                        {
                            bool matchProperty(IXamlAstNode node, IXamlType styledElementType, string propertyName)
                            {
                                return((node is XamlPropertyAssignmentNode p &&
                                        p.Property.DeclaringType == styledElementType && p.Property.Name == propertyName)
                                       ||
                                       (node is XamlManipulationGroupNode m && m.Children.Count > 0 &&
                                        m.Children[0] is XamlPropertyAssignmentNode pm &&
                                        pm.Property.DeclaringType == styledElementType && pm.Property.Name == propertyName));
                            }

                            string getResourceValue_xKey(XamlPropertyAssignmentNode node)
                            => node.Values.Count == 2 && node.Values[0] is XamlAstTextNode t ? t.Text : "";

                            IXamlType getResourceValue_Type(XamlPropertyAssignmentNode node, IXamlType xamlType)
                            => node.Values.Count == 2 ? node.Values[1].Type.GetClrType() : xamlType;

                            IEnumerable <XamlPropertyAssignmentNode> getResourceValues(IXamlAstNode node)
                            {
                                if (node is XamlPropertyAssignmentNode propertyNode)
                                {
                                    if (propertyNode.Values.Count == 1 &&
                                        propertyNode.Values[0] is XamlAstConstructableObjectNode obj &&
                                        obj.Type.GetClrType().FullName == "Avalonia.Controls.ResourceDictionary")
                                    {
                                        foreach (var r in obj.Children.SelectMany(c => getResourceValues(c)))
                                        {
                                            yield return(r);
                                        }
                                    }
                                    else
                                    {
                                        yield return(propertyNode);
                                    }
                                }
                                else if (node is XamlManipulationGroupNode m)
                                {
                                    foreach (var r in m.Children.OfType <XamlPropertyAssignmentNode>())
                                    {
                                        yield return(r);
                                    }
                                }
                            }

                            string key = keyNode.Text;

                            var styledElement = context.GetAvaloniaTypes().StyledElement;
                            var resource      = context.ParentNodes()
                                                .OfType <XamlAstConstructableObjectNode>()
                                                .Where(o => styledElement.IsAssignableFrom(o.Type.GetClrType()))
                                                .Select(o => o.Children.FirstOrDefault(p => matchProperty(p, styledElement, "Resources")))
                                                .Where(r => r != null)
                                                .SelectMany(r => getResourceValues(r))
                                                .FirstOrDefault(r => getResourceValue_xKey(r) == key);

                            if (resource != null)
                            {
                                startType = getResourceValue_Type(resource, startType);
                            }
                        }
        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);
        }
Exemple #20
0
        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;

                    case SelectorGrammar.NestingSyntax:
                        var parentTargetType = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();

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

                        result = new XamlIlNestingSelector(result, parentTargetType.TargetType.GetClrType());
                        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;
                }