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