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 = GetAllDefinedProperties(targetType).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;
                    foreach (var currentType in TraverseTypeHierarchy(targetType))
                    {
                        var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute");
                        if (defaultMemberAttribute != null)
                        {
                            property = currentType.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()))
                                                .Skip(1)
                                                .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 (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;

                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)
                    {
                        var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute;

                        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 (pa.Property.CustomAttributes.Any(a => a.Type == templateDataTypeAttribute) &&
                                 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 (context.GetAvaloniaTypes().IDataTemplate.IsAssignableFrom(on.Type.GetClrType()) &&
                        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)
                        {
                            var parentType = parentObject.Type.GetClrType();

                            if (context.GetAvaloniaTypes().IItemsPresenterHost.IsDirectlyAssignableFrom(parentType) ||
                                context.GetAvaloniaTypes().ItemsRepeater.IsDirectlyAssignableFrom(parentType))
                            {
                                inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject);
                            }
                        }

                        if (inferredDataContextTypeNode is null)
                        {
                            inferredDataContextTypeNode = new AvaloniaXamlIlUninferrableDataContextMetadataNode(on);
                        }
                    }
                }

                return(directiveDataContextTypeNode ?? inferredDataContextTypeNode ?? node);
            }
Exemple #4
0
        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 relativeSourceProperty = syntheticCompiledBindingProperties
                                         .FirstOrDefault(v =>
                                                         v.Property is AvaloniaSyntheticCompiledBindingProperty prop &&
                                                         prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource);

            var sourceProperty = binding.Children.OfType <XamlAstXamlPropertyValueNode>()
                                 .FirstOrDefault(v =>
                                                 v.Property is XamlAstClrProperty prop &&
                                                 prop.Name == "Source");

            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 != null && convertedNode != null)
            {
                throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
            }

            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 (relativeSourceProperty != null)
            {
                binding.Children.Remove(relativeSourceProperty);
            }

            return(convertedNode);
        }
Exemple #5
0
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
            {
                var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding);

                if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText)
                {
                    var reader = new CharacterReader(bindingPathText.Text.AsSpan());
                    var(nodes, _) = BindingExpressionGrammar.Parse(ref reader);

                    if (convertedNode != null)
                    {
                        nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
                    }

                    binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
                }
                else
                {
                    var bindingPathAssignment = binding.Children.OfType <XamlAstXamlPropertyValueNode>()
                                                .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path");

                    if (bindingPathAssignment != null && bindingPathAssignment.Values[0] is XamlAstTextNode pathValue)
                    {
                        var reader = new CharacterReader(pathValue.Text.AsSpan());
                        var(nodes, _) = BindingExpressionGrammar.Parse(ref reader);

                        if (convertedNode != null)
                        {
                            nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
                        }

                        bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
                    }
                }
            }

            return(node);
        }
        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 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))
            {
                try
                {
                    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);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
                }
            }

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

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

                    return(true);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
                }
            }

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

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

                    return(true);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
                }
            }

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

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

                    return(true);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
                }
            }

            if (type.Equals(types.Matrix))
            {
                try
                {
                    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);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
                }
            }

            if (type.Equals(types.CornerRadius))
            {
                try
                {
                    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);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
                }
            }

            if (type.Equals(types.Color))
            {
                if (!Color.TryParse(text, out Color color))
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
                }

                result = new XamlStaticOrTargetedReturnMethodCallNode(node,
                                                                      type.GetMethod(
                                                                          new FindMethodMethodSignature("FromUInt32", type, types.UInt)
                {
                    IsStatic = true
                }),
                                                                      new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });

                return(true);
            }

            if (type.Equals(types.GridLength))
            {
                try
                {
                    var gridLength = GridLength.Parse(text);

                    result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);

                    return(true);
                }
                catch
                {
                    throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
                }
            }

            if (type.Equals(types.Cursor))
            {
                if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode))
                {
                    var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false);

                    result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List <IXamlAstValueNode> {
                        enumConstantNode
                    });

                    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 (!(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));
        }