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); }
public XamlIlAstClrProperty(IXamlIlLineInfo lineInfo, IXamlIlProperty property, XamlIlTransformerConfiguration cfg) : base(lineInfo) { Name = property.Name; Getter = property.Getter; if (property.Setter != null) { Setters.Add(new XamlIlDirectCallPropertySetter(property.Setter)); } CustomAttributes = property.CustomAttributes.ToList(); DeclaringType = (property.Getter ?? property.Setter)?.DeclaringType; var typeConverterAttributes = cfg.GetCustomAttribute(property, cfg.TypeMappings.TypeConverterAttributes); if (typeConverterAttributes != null) { foreach (var attr in typeConverterAttributes) { var typeConverter = XamlIlTransformHelpers.TryGetTypeConverterFromCustomAttribute(cfg, attr); if (typeConverter != null) { TypeConverters[property.PropertyType] = typeConverter; break; } } } }
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { if (node is XamlIlAstClrProperty prop && prop.Getter != null) { foreach (var adder in XamlIlTransformHelpers.FindPossibleAdders(context, prop.Getter.ReturnType)) { prop.Setters.Add(new AdderSetter(prop.Getter, adder)); } } return(node); }
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { if (node is XamlIlAstObjectNode ni) { XamlIlAstXamlPropertyValueNode propertyNode = null; for (var c = ni.Children.Count - 1; c >= 0; c--) { var child = ni.Children[c]; if (child is IXamlIlAstValueNode valueNode) { if (propertyNode == null) { var contentProperty = context.Configuration.FindContentProperty(ni.Type.GetClrType()); if (contentProperty != null) { propertyNode = new XamlIlAstXamlPropertyValueNode(ni, new XamlIlAstClrProperty(ni, contentProperty, context.Configuration), Array.Empty <IXamlIlAstValueNode>()); } else { var adders = XamlIlTransformHelpers.FindPossibleAdders(context, ni.Type.GetClrType()); if (adders.Count == 0) { throw new XamlIlParseException( $"No Content property or any Add methods found for type {ni.Type.GetClrType().GetFqn()}", child); } propertyNode = new XamlIlAstXamlPropertyValueNode(ni, new XamlIlAstClrProperty(ni, "Content", ni.Type.GetClrType(), null, adders.Select(a => new XamlIlDirectCallPropertySetter(a) { BinderParameters = { AllowMultiple = true } })), Array.Empty <IXamlIlAstValueNode>()); } } // We are going in reverse order, so insert at the beginning propertyNode.Values.Insert(0, valueNode); ni.Children.RemoveAt(c); } } if (propertyNode != null) { ni.Children.Add(propertyNode); } } return(node); }
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { if (node is XamlIlAstXamlPropertyValueNode pn) { var assignments = XamlIlTransformHelpers.GeneratePropertyAssignments(context, pn.Property.GetClrProperty(), pn.Values); return(new XamlIlManipulationGroupNode(pn) { Children = assignments }); } else { return(node); } }
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { if (node is IXamlIlAstValueNode vn) { if (context.ParentNodes().FirstOrDefault() is XamlIlMarkupExtensionNode) { return(node); } if (XamlIlTransformHelpers.TryConvertMarkupExtension(context, vn, out var rv)) { return(rv); } } return(node); }
void SubTransform(XamlIlAstTransformationContext context, XamlIlAstObjectNode ni) { var valueIndexes = new List <int>(); for (var c = 0; c < ni.Children.Count; c++) { if (ni.Children[c] is IXamlIlAstValueNode) { valueIndexes.Add(c); } } if (valueIndexes.Count == 0) { return; } var type = ni.Type.GetClrType(); IXamlIlAstValueNode VNode(int ind) => (IXamlIlAstValueNode)ni.Children[ind]; var contentProperty = context.Configuration.FindContentProperty(type); if (contentProperty == null) { foreach (var ind in valueIndexes) { if (XamlIlTransformHelpers.TryCallAdd(context, null, type, VNode(ind), out var addCall)) { ni.Children[ind] = addCall; } else { throw new XamlIlLoadException( $"Type `{type.GetFqn()} does not have content property and suitable Add({VNode(ind).Type.GetClrType().GetFqn()}) not found", VNode(ind)); } } } else { XamlIlTransformHelpers.GeneratePropertyAssignments(context, contentProperty, valueIndexes.Count, num => VNode(valueIndexes[num]), (i, v) => ni.Children[valueIndexes[i]] = v); } }
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); }