private static bool GetRelativeSourceObjectFromAssignment(
            AstTransformationContext context,
            XamlAstXamlPropertyValueNode relativeSourceProperty,
            out XamlAstObjectNode relativeSourceObject)
        {
            relativeSourceObject = null;
            if (relativeSourceProperty is null)
            {
                return(false);
            }

            if (relativeSourceProperty.Values[0] is XamlMarkupExtensionNode me)
            {
                if (me.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
                {
                    throw new XamlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{me.Type.GetClrType().GetFqn()}'", me);
                }

                relativeSourceObject = (XamlAstObjectNode)me.Value;
                return(true);
            }

            if (relativeSourceProperty.Values[0] is XamlAstObjectNode on)
            {
                if (on.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
                {
                    throw new XamlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{on.Type.GetClrType().GetFqn()}'", on);
                }

                relativeSourceObject = on;
                return(true);
            }

            return(false);
        }
Пример #2
0
        public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
        {
            if (!(node is XamlAstXamlPropertyValueNode propertyValueNode))
            {
                return(node);
            }

            if (!(propertyValueNode.Property is XamlAstClrProperty clrProperty))
            {
                return(node);
            }

            IEnumerable <IXamlCustomAttribute> attributes = propertyValueNode.Property.GetClrProperty().CustomAttributes;

            if (propertyValueNode.Property is XamlAstClrProperty referenceNode &&
                referenceNode.Getter != null)
            {
                attributes = attributes.Concat(referenceNode.Getter.CustomAttributes);
            }

            if (attributes.All(attribute => attribute.Type.FullName != "Avalonia.Controls.ResolveByNameAttribute"))
            {
                return(node);
            }

            if (propertyValueNode.Values.Count != 1 || !(propertyValueNode.Values.First() is XamlAstTextNode))
            {
                return(node);
            }

            var newNode = new XamlAstObjectNode(
                propertyValueNode.Values[0],
                new XamlAstClrTypeReference(propertyValueNode.Values[0],
                                            context.GetAvaloniaTypes().ResolveByNameExtension, true))
            {
                Arguments = new List <IXamlAstValueNode> {
                    propertyValueNode.Values[0]
                }
            };

            if (XamlTransformHelpers.TryConvertMarkupExtension(context, newNode, out var extensionNode))
            {
                propertyValueNode.Values[0] = extensionNode;
            }

            return(node);
        }
        IXamlConstructor TransformArgumentsAndGetConstructor(
            AstTransformationContext context,
            XamlAstObjectNode n)
        {
            var type = n.Type.GetClrType();

            var argTypes = n.Arguments.Select(a => a.Type.GetClrType()).ToList();
            var ctor     = type.FindConstructor(argTypes);

            if (ctor == null)
            {
                if (argTypes.Count != 0)
                {
                    ctor = type.Constructors.FirstOrDefault(x =>
                                                            !x.IsStatic && x.IsPublic && x.Parameters.Count == argTypes.Count);
                }

                if (ctor == null)
                {
                    throw new XamlLoadException(
                              $"Unable to find public constructor for type {type.GetFqn()}({string.Join(", ", argTypes.Select(at => at.GetFqn()))})",
                              n);
                }
            }

            for (var c = 0; c < n.Arguments.Count; c++)
            {
                if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, n.Arguments[c], ctor.Parameters[c], out var arg))
                {
                    throw new XamlLoadException(
                              $"Unable to convert {n.Arguments[c].Type.GetClrType().GetFqn()} to {ctor.Parameters[c].GetFqn()} for constructor of {n.Type.GetClrType().GetFqn()}",
                              n.Arguments[c]);
                }
                n.Arguments[c] = arg;
            }

            return(ctor);
        }
        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);
        }
        public static IXamlAstValueNode Parse(IXamlLineInfo li, string ext,
                                              Func <string, XamlAstXmlTypeReference> typeResolver)
        {
            var ctx     = new MeScannerContext(typeResolver, li);
            var scanner = new MeScanner(ctx, ext, li.Line, li.Position);

            var currentTypeStack = new Stack <MeScannerTypeName>();

            IXamlAstValueNode ReadExtension()
            {
                if (scanner.Token != MeTokenType.Open)
                {
                    throw new MeScannerParseException("Unexpected token " + scanner.Token);
                }
                scanner.Read();
                if (scanner.Token != MeTokenType.TypeName)
                {
                    throw new MeScannerParseException("Unexpected token " + scanner.Token);
                }
                var extType = scanner.TokenType;

                extType.TypeReference.IsMarkupExtension = true;
                currentTypeStack.Push(ctx.CurrentType);
                ctx.CurrentType = extType;

                var rv = new XamlAstObjectNode(li, extType.TypeReference);


                while (true)
                {
                    scanner.Read();
                    if (scanner.Token == MeTokenType.Close)
                    {
                        break;
                    }
                    else if (scanner.Token == MeTokenType.PropertyName)
                    {
                        var prop = scanner.TokenProperty;
                        scanner.Read();
                        if (scanner.Token != MeTokenType.EqualSign)
                        {
                            throw new MeScannerParseException("Unexpected token " + scanner.Token);
                        }
                        var propValue = Read();
                        rv.Children.Add(new XamlAstXamlPropertyValueNode(li, prop, propValue));
                    }
                    else if (scanner.Token == MeTokenType.String || scanner.Token == MeTokenType.QuotedMarkupExtension ||
                             scanner.Token == MeTokenType.Open)
                    {
                        if (rv.Children.Count != 0)
                        {
                            throw new MeScannerParseException("Unexpected token after property list " + scanner.Token);
                        }
                        rv.Arguments.Add(ReadCurrent());
                    }
                    else if (scanner.Token == MeTokenType.Comma)
                    {
                        continue;
                    }
                    else
                    {
                        throw new MeScannerParseException("Unexpected token " + scanner.Token);
                    }
                }

                ctx.CurrentType = currentTypeStack.Pop();
                return(rv);
            }

            IXamlAstValueNode Read()
            {
                scanner.Read();
                return(ReadCurrent());
            }

            IXamlAstValueNode ReadCurrent()
            {
                if (scanner.Token == MeTokenType.String)
                {
                    return(new XamlAstTextNode(li, scanner.TokenText, true));
                }
                if (scanner.Token == MeTokenType.Open)
                {
                    return(ReadExtension());
                }
                if (scanner.Token == MeTokenType.QuotedMarkupExtension)
                {
                    return(Parse(li, scanner.TokenText, typeResolver));
                }
                throw new MeScannerParseException("Unexpected token " + scanner.Token);
            }

            return(Read());
        }
Пример #6
0
            XamlAstObjectNode ParseNewInstance(XElement el, bool root)
            {
                if (el.Name.LocalName.Contains("."))
                {
                    throw ParseError(el, "Dots aren't allowed in type names");
                }
                var type = GetTypeReference(el);
                var i    = new XamlAstObjectNode(el.AsLi(), type);

                foreach (var attr in el.Attributes())
                {
                    if (attr.Name.NamespaceName == "http://www.w3.org/2000/xmlns/" ||
                        (attr.Name.NamespaceName == "" && attr.Name.LocalName == "xmlns"))
                    {
                        if (!root)
                        {
                            throw ParseError(attr,
                                             "xmlns declarations are only allowed on the root element to preserve memory");
                        }
                    }
                    else if (attr.Name.NamespaceName.StartsWith("http://www.w3.org"))
                    {
                        // Silently ignore all xml-parser related attributes
                    }
                    // Parse type arguments
                    else if (attr.Name.NamespaceName == XamlNamespaces.Xaml2006 &&
                             attr.Name.LocalName == "TypeArguments")
                    {
                        type.GenericArguments = ParseTypeArguments(attr.Value, el, attr.AsLi());
                    }
                    // Parse as a directive
                    else if (attr.Name.NamespaceName != "" && !attr.Name.LocalName.Contains("."))
                    {
                        i.Children.Add(new XamlAstXmlDirective(el.AsLi(),
                                                               attr.Name.NamespaceName, attr.Name.LocalName, new[]
                        {
                            ParseTextValueOrMarkupExtension(attr.Value, el, attr.AsLi())
                        }
                                                               ));
                    }
                    // Parse as a property
                    else
                    {
                        var pname = attr.Name.LocalName;
                        var ptype = i.Type;

                        if (pname.Contains("."))
                        {
                            var parts = pname.Split(new[] { '.' }, 2);
                            pname = parts[1];
                            var ns = attr.Name.Namespace == "" ? el.GetDefaultNamespace().NamespaceName : attr.Name.NamespaceName;
                            ptype = new XamlAstXmlTypeReference(el.AsLi(), ns, parts[0]);
                        }

                        i.Children.Add(new XamlAstXamlPropertyValueNode(el.AsLi(),
                                                                        new XamlAstNamePropertyReference(el.AsLi(), ptype, pname, type),
                                                                        ParseTextValueOrMarkupExtension(attr.Value, el, attr.AsLi())));
                    }
                }


                foreach (var node in el.Nodes())
                {
                    if (node is XElement elementNode && elementNode.Name.LocalName.Contains("."))
                    {
                        if (elementNode.HasAttributes)
                        {
                            throw ParseError(node, "Attributes aren't allowed on element properties");
                        }
                        var pair = elementNode.Name.LocalName.Split(new[] { '.' }, 2);
                        i.Children.Add(new XamlAstXamlPropertyValueNode(el.AsLi(), new XamlAstNamePropertyReference
                                                                        (
                                                                            el.AsLi(),
                                                                            new XamlAstXmlTypeReference(el.AsLi(), elementNode.Name.NamespaceName,
                                                                                                        pair[0]), pair[1], type
                                                                        ),
                                                                        ParseValueNodeChildren(elementNode)
                                                                        ));
                    }