public XamlAstClrProperty(IXamlLineInfo lineInfo, IXamlProperty property, TransformerConfiguration cfg) : base(lineInfo) { Name = property.Name; Getter = property.Getter; if (property.Setter != null) { Setters.Add(new XamlDirectCallPropertySetter(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 = XamlTransformHelpers.TryGetTypeConverterFromCustomAttribute(cfg, attr); if (typeConverter != null) { TypeConverters[property.PropertyType] = typeConverter; break; } } } }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (!(node is XamlAstObjectNode on)) { return(node); } var nonDirectiveChildren = on.Children.Where(a => !(a is XamlAstXmlDirective)).ToList(); if (on.Arguments.Count != 0 || nonDirectiveChildren.Count != 1 || !(nonDirectiveChildren[0] is IXamlAstValueNode vn) || !vn.Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String)) { return(node); } if (XamlTransformHelpers.TryGetCorrectlyTypedValue(context, vn, on.Type.GetClrType(), out var rv)) { if (nonDirectiveChildren.Count != on.Children.Count) { rv = new XamlValueWithManipulationNode(rv, rv, new XamlManipulationGroupNode(rv, on.Children.OfType <XamlAstXmlDirective>())); } return(rv); } if (on.Type.GetClrType().IsValueType) { throw new XamlLoadException( $"Unable to convert value {(vn as XamlAstTextNode)?.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 IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstClrProperty prop) { if (prop.DeclaringType.Assembly == context.GetWinUITypes().WinUIControlsAssembly) { IXamlField propertyIndexMaybe = context.GetWinUITypes().XamlPropertyIndex.Fields.FirstOrDefault(f => f.Name == $"{prop.DeclaringType.Name}_{prop.Name}"); if (propertyIndexMaybe != null) { prop.Setters.Insert(0, new XamlDirectSetter(context.GetWinUITypes(), prop.Getter.ReturnType, prop.DeclaringType, propertyIndexMaybe)); foreach (var adder in XamlTransformHelpers.FindPossibleAdders(context, prop.Getter.ReturnType)) { if (adder.Parameters.Count == 1) { prop.Setters.Add(new XamlDirectAdderSetter(context.GetWinUITypes(), adder.Parameters[0], prop.DeclaringType, propertyIndexMaybe)); } } return(prop); } IXamlField eventIndexMaybe = context.GetWinUITypes().XamlEventIndex.Fields.FirstOrDefault(f => f.Name == $"{prop.DeclaringType.Name}_{prop.Name}"); if (eventIndexMaybe != null) { prop.Setters.Insert(0, new XamlDirectEventSetter(context.GetWinUITypes(), prop.Setters[0].Parameters[0], prop.DeclaringType, eventIndexMaybe)); return(prop); } } } return(node); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstObjectNode ni) { XamlAstXamlPropertyValueNode propertyNode = null; for (var c = ni.Children.Count - 1; c >= 0; c--) { var child = ni.Children[c]; if (child is IXamlAstValueNode valueNode) { if (propertyNode == null) { var contentProperty = context.Configuration.FindContentProperty(ni.Type.GetClrType()); if (contentProperty != null) { propertyNode = new XamlAstXamlPropertyValueNode(ni, new XamlAstClrProperty(ni, contentProperty, context.Configuration), Array.Empty <IXamlAstValueNode>()); } else { var adders = XamlTransformHelpers.FindPossibleAdders(context, ni.Type.GetClrType()); if (adders.Count == 0) { // If there's no content property, strip all whitespace-only nodes and continue WhitespaceNormalization.RemoveWhitespaceNodes(ni.Children); if (!ni.Children.Contains(child)) { continue; } throw new XamlParseException( $"No Content property or any Add methods found for type {ni.Type.GetClrType().GetFqn()}", child); } propertyNode = new XamlAstXamlPropertyValueNode(ni, new XamlAstClrProperty(ni, "Content", ni.Type.GetClrType(), null, adders.Select(a => new XamlDirectCallPropertySetter(a) { BinderParameters = { AllowMultiple = true } })), Array.Empty <IXamlAstValueNode>()); } } // 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 IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstClrProperty prop && prop.Getter != null) { foreach (var adder in XamlTransformHelpers.FindPossibleAdders(context, prop.Getter.ReturnType)) { prop.Setters.Add(new AdderSetter(prop.Getter, adder)); } } return(node); }
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); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is IXamlAstValueNode vn) { if (context.ParentNodes().FirstOrDefault() is XamlMarkupExtensionNode) { return(node); } if (XamlTransformHelpers.TryConvertMarkupExtension(context, vn, out var rv)) { return(rv); } } 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 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 = targetType.GetAllProperties().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; for (var currentType = targetType; currentType != null; currentType = currentType.BaseType) { var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute"); if (defaultMemberAttribute != null) { property = targetType.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())) .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 XamlAstXamlPropertyValueNode valueNode) { var property = valueNode.Property.GetClrProperty(); var assignments = new List <XamlPropertyAssignmentNode>(); WhitespaceNormalization.RemoveWhitespaceNodes(valueNode.Values); foreach (var v in valueNode.Values) { var keyNode = FindAndRemoveKey(v); var arguments = new List <IXamlAstValueNode>(); 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++) { IXamlType convertedTo = null; for (var s = 0; s < filteredSetters.Count;) { var setter = filteredSetters[s]; if (convertedTo == null) { if (!XamlTransformHelpers.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 XamlLoadException( $"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++; } } } XamlPropertyAssignmentNode CreateAssignment() { var matchedSetters = new List <IXamlPropertySetter>(); foreach (var setter in filteredSetters) { bool CanAssign(IXamlAstValueNode value, IXamlType type) { // Don't allow x:Null if (!setter.BinderParameters.AllowXNull && XamlPseudoType.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 (XamlTransformHelpers.TryConvertValue(context, valueArg, setterType, property, out var converted)) { arguments[valueArgIndex] = converted; return(new XamlPropertyAssignmentNode(valueNode, property, new[] { setter }, arguments)); } } if (matchedSetters.Count > 0) { return(new XamlPropertyAssignmentNode(v, property, matchedSetters, arguments)); } throw new XamlLoadException( $"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 XamlLoadException( $"Unable to find a setter that allows multiple assignments to the property {ass.Property.Name} of type {ass.Property.DeclaringType.GetFqn()}", node); } } } return(new XamlManipulationGroupNode(valueNode, assignments)); } return(node); }
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); 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, 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.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)); }