Ejemplo n.º 1
0
        public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
        {
            if (!(node is XamlIlAstObjectNode on))
            {
                return(node);
            }
            var nonDirectiveChildren = on.Children.Where(a => !(a is XamlIlAstXmlDirective)).ToList();

            if (on.Arguments.Count != 0 ||
                nonDirectiveChildren.Count != 1 ||
                !(nonDirectiveChildren[0] is IXamlIlAstValueNode vn) ||
                !vn.Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
            {
                return(node);
            }

            if (XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, vn, on.Type.GetClrType(), out var rv))
            {
                if (nonDirectiveChildren.Count != on.Children.Count)
                {
                    rv = new XamlIlValueWithManipulationNode(rv, rv,
                                                             new XamlIlManipulationGroupNode(rv, on.Children.OfType <XamlIlAstXmlDirective>()));
                }
                return(rv);
            }

            if (on.Type.GetClrType().IsValueType)
            {
                throw new XamlIlLoadException(
                          $"Unable to convert value {(vn as XamlIlAstTextNode)?.Text}) to {on.Type.GetClrType()}", vn);
            }

            // Parser not found, isn't a value type, probably a regular object creation node with text content
            return(node);
        }
Ejemplo n.º 2
0
        IXamlIlConstructor TransformArgumentsAndGetConstructor(XamlIlAstTransformationContext context,
                                                               XamlIlAstObjectNode 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 XamlIlLoadException(
                              $"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 (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, n.Arguments[c], ctor.Parameters[c], out var arg))
                {
                    throw new XamlIlLoadException(
                              $"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);
        }
        public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
        {
            if (!(node is XamlIlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Style"))
            {
                return(node);
            }

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

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

            if (pn.Values.Count != 1)
            {
                throw new XamlIlParseException("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 XamlIlAstTextNode tn))
            {
                throw new XamlIlParseException("Selector property should be a text node", node);
            }

            var selectorType = pn.Property.GetClrProperty().Getter.ReturnType;
            var initialNode  = new XamlIlSelectorInitialNode(node, selectorType);

            XamlIlSelectorNode Create(IEnumerable <SelectorGrammar.ISyntax> syntax,
                                      Func <string, string, XamlIlAstClrTypeReference> 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 XamlIlParseException("Property selectors must be applied to a type.", node);
                        }

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

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

                        if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context,
                                                                              new XamlIlAstTextNode(node, property.Value, context.Configuration.WellKnownTypes.String),
                                                                              targetProperty.PropertyType, out var typedValue))
                        {
                            throw new XamlIlParseException(
                                      $"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}",
                                      node);
                        }

                        result = new XamlIlPropertyEqualsSelector(result, targetProperty, 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.CommaSyntax comma:
                        if (results == null)
                        {
                            results = new XamlIlOrSelectorNode(node, selectorType);
                        }
                        results.Add(result);
                        result = initialNode;
                        break;

                    default:
                        throw new XamlIlParseException($"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 XamlIlParseException("Unable to parse selector: " + e.Message, node);
            }

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

            pn.Values[0] = selector;

            return(new AvaloniaXamlIlTargetTypeMetadataNode(on,
                                                            new XamlIlAstClrTypeReference(selector, selector.TargetType, false),
                                                            AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style));
        }
        public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
        {
            if (node is XamlIlAstXamlPropertyValueNode valueNode)
            {
                var property    = valueNode.Property.GetClrProperty();
                var assignments = new List <XamlIlPropertyAssignmentNode>();
                foreach (var v in valueNode.Values)
                {
                    var keyNode   = FindAndRemoveKey(v);
                    var arguments = new List <IXamlIlAstValueNode>();

                    if (keyNode != null)
                    {
                        arguments.Add(keyNode);
                    }
                    arguments.Add(v);


                    // Pre-filter setters by non-last argument
                    var filteredSetters = property.Setters.Where(s => s.Parameters.Count == arguments.Count)
                                          .ToList();

                    if (arguments.Count > 1)
                    {
                        for (var c = 0; c < arguments.Count - 1; c++)
                        {
                            IXamlIlType convertedTo = null;
                            for (var s = 0; s < filteredSetters.Count;)
                            {
                                var setter = filteredSetters[s];
                                if (convertedTo == null)
                                {
                                    if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, arguments[c],
                                                                                          setter.Parameters[c], out var converted))
                                    {
                                        filteredSetters.RemoveAt(c);
                                        continue;
                                    }
                                    else
                                    {
                                        convertedTo  = converted.Type.GetClrType();
                                        arguments[c] = converted;
                                    }
                                }
                                else
                                {
                                    if (!setter.Parameters[c].IsAssignableFrom(convertedTo))
                                    {
                                        throw new XamlIlLoadException(
                                                  $"Runtime setter selection is not supported for non-last setter arguments (e. g. x:Key) and can not downcast argument {c} of the setter from {convertedTo} to {setter.Parameters[c]}",
                                                  arguments[c]);
                                    }
                                }
                                s++;
                            }
                        }
                    }

                    XamlIlPropertyAssignmentNode CreateAssignment()
                    {
                        var matchedSetters = new List <IXamlIlPropertySetter>();

                        foreach (var setter in filteredSetters)
                        {
                            bool CanAssign(IXamlIlAstValueNode value, IXamlIlType type)
                            {
                                // Don't allow x:Null
                                if (!setter.BinderParameters.AllowXNull &&
                                    XamlIlPseudoType.Null.Equals(value.Type.GetClrType()))
                                {
                                    return(false);
                                }

                                // Direct cast
                                if (type.IsAssignableFrom(value.Type.GetClrType()))
                                {
                                    return(true);
                                }

                                // Upcast from System.Object
                                if (value.Type.GetClrType().Equals(context.Configuration.WellKnownTypes.Object))
                                {
                                    return(true);
                                }

                                return(false);
                            }

                            var valueArgIndex = arguments.Count - 1;
                            var valueArg      = arguments[valueArgIndex];
                            var setterType    = setter.Parameters[valueArgIndex];

                            if (CanAssign(valueArg, setterType))
                            {
                                matchedSetters.Add(setter);
                            }
                            // Converted value have more priority than custom setters, so we just create a setter without an alternative
                            else if (XamlIlTransformHelpers.TryConvertValue(context, valueArg, setterType, property,
                                                                            out var converted))
                            {
                                arguments[valueArgIndex] = converted;
                                return(new XamlIlPropertyAssignmentNode(valueNode,
                                                                        property, new[] { setter }, arguments));
                            }
                        }

                        if (matchedSetters.Count > 0)
                        {
                            return(new XamlIlPropertyAssignmentNode(v, property, matchedSetters, arguments));
                        }

                        throw new XamlIlLoadException(
                                  $"Unable to find suitable setter or adder for property {property.Name} of type {property.DeclaringType.GetFqn()} for argument {v.Type.GetClrType().GetFqn()}"
                                  + (keyNode != null ? $" and x:Key of type {keyNode.Type.GetClrType()}" : null)
                                  + ", available setter parameter lists are:\n" + string.Join("\n",
                                                                                              filteredSetters.Select(setter =>
                                                                                                                     string.Join(", ", setter.Parameters.Select(p => p.FullName))))
                                  , v);
                    }

                    assignments.Add(CreateAssignment());
                }

                if (assignments.Count == 1)
                {
                    return(assignments[0]);
                }

                if (assignments.Count > 1)
                {
                    // Skip the first one, since we only care about further setters, e. g. the following is perfectly valid:
                    // <Foo.Bar>
                    //   <SomeList/>
                    //   <ListItem/>
                    //   <ListItem/>
                    // </Foo.Bar>
                    // <SomeList/> would be foo.Bar = new SomeList() and <ListItem/> would be foo.Bar.Add(new ListItem());
                    foreach (var ass in assignments.Skip(1))
                    {
                        ass.PossibleSetters = ass.PossibleSetters.Where(s => s.BinderParameters.AllowMultiple).ToList();
                        if (ass.PossibleSetters.Count == 0)
                        {
                            throw new XamlIlLoadException(
                                      $"Unable to find a setter that allows multiple assignments to the property {ass.Property.Name} of type {ass.Property.DeclaringType.GetFqn()}",
                                      node);
                        }
                    }
                }

                return(new XamlIlManipulationGroupNode(valueNode, assignments));
            }

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

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

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

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

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


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

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

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

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


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

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

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

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

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

            return(node);
        }