protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen) { if (!XamlIlAvaloniaPropertyHelper.Emit(context, codeGen, Property)) { throw new XamlIlLoadException( $"{Property.Name} of {(Property.Setter ?? Property.Getter).DeclaringType.GetFqn()} doesn't seem to be an AvaloniaProperty", this); } context.Emit(Value, codeGen, context.Configuration.WellKnownTypes.Object); EmitCall(context, codeGen, m => m.Name == "PropertyEquals" && m.Parameters.Count == 3 && m.Parameters[1].FullName == "Avalonia.AvaloniaProperty" && m.Parameters[2].Equals(context.Configuration.WellKnownTypes.Object)); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) { return(node); } var targetTypeNode = context.ParentNodes() .OfType <AvaloniaXamlIlTargetTypeMetadataNode>() .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) ?? throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType", node); IXamlType propType = null; var property = @on.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property"); if (property != null) { var propertyName = property.Values.OfType <XamlAstTextNode>().FirstOrDefault()?.Text; if (propertyName == null) { throw new XamlParseException("Setter.Property must be a string", node); } var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, new XamlAstClrTypeReference(targetTypeNode, targetTypeNode.TargetType.GetClrType(), false), property.Values[0]); property.Values = new List <IXamlAstValueNode> { avaloniaPropertyNode }; propType = avaloniaPropertyNode.AvaloniaPropertyType; } else { var propertyPath = on.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath"); if (propertyPath == null) { throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node); } if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn && ppn.PropertyType != null) { propType = ppn.PropertyType; }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstXamlPropertyValueNode pv && pv.Values.Count == 1 && pv.Values[0] is XamlAstTextNode text && pv.Property.GetClrProperty().Getter?.ReturnType .Equals(context.GetAvaloniaTypes().PropertyPath) == true ) { var parentScope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>() .FirstOrDefault(); if (parentScope == null) { throw new XamlX.XamlParseException("No target type scope found for property path", text); } if (parentScope.ScopeType != AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) { throw new XamlX.XamlParseException("PropertyPath is currently only valid for styles", pv); } IEnumerable <PropertyPathGrammar.ISyntax> parsed; try { parsed = PropertyPathGrammar.Parse(text.Text); } catch (Exception e) { throw new XamlX.XamlParseException("Unable to parse PropertyPath: " + e.Message, text); } var elements = new List <IXamlIlPropertyPathElementNode>(); IXamlType currentType = parentScope.TargetType.GetClrType(); var expectProperty = true; var expectCast = true; var expectTraversal = false; var types = context.GetAvaloniaTypes(); IXamlType GetType(string ns, string name) { return(TypeReferenceResolver.ResolveType(context, $"{ns}:{name}", false, text, true).GetClrType()); } void HandleProperty(string name, string typeNamespace, string typeName) { if (!expectProperty || currentType == null) { throw new XamlX.XamlParseException("Unexpected property node", text); } var propertySearchType = typeName != null?GetType(typeNamespace, typeName) : currentType; IXamlIlPropertyPathElementNode prop = null; var avaloniaPropertyFieldName = name + "Property"; var avaloniaPropertyField = propertySearchType.GetAllFields().FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldName); if (avaloniaPropertyField != null) { prop = new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyField, XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyField, types, text)); } else { var clrProperty = propertySearchType.GetAllProperties().FirstOrDefault(p => p.Name == name); prop = new XamlIClrPropertyPathElementNode(clrProperty); } if (prop == null) { throw new XamlX.XamlParseException( $"Unable to resolve property {name} on type {propertySearchType.GetFqn()}", text); } currentType = prop.Type; elements.Add(prop); expectProperty = false; expectTraversal = expectCast = true; } foreach (var ge in parsed) { if (ge is PropertyPathGrammar.ChildTraversalSyntax) { if (!expectTraversal) { throw new XamlX.XamlParseException("Unexpected child traversal .", text); } elements.Add(new XamlIlChildTraversalPropertyPathElementNode()); expectTraversal = expectCast = false; expectProperty = true; } else if (ge is PropertyPathGrammar.EnsureTypeSyntax ets) { if (!expectCast) { throw new XamlX.XamlParseException("Unexpected cast node", text); } currentType = GetType(ets.TypeNamespace, ets.TypeName); elements.Add(new XamlIlCastPropertyPathElementNode(currentType, true)); expectProperty = false; expectCast = expectTraversal = true; } else if (ge is PropertyPathGrammar.CastTypeSyntax cts) { if (!expectCast) { throw new XamlX.XamlParseException("Unexpected cast node", text); } //TODO: Check if cast can be done currentType = GetType(cts.TypeNamespace, cts.TypeName); elements.Add(new XamlIlCastPropertyPathElementNode(currentType, false)); expectProperty = false; expectCast = expectTraversal = true; } else if (ge is PropertyPathGrammar.PropertySyntax ps) { HandleProperty(ps.Name, null, null); } else if (ge is PropertyPathGrammar.TypeQualifiedPropertySyntax tqps) { HandleProperty(tqps.Name, tqps.TypeNamespace, tqps.TypeName); } else { throw new XamlX.XamlParseException("Unexpected node " + ge, text); } } var propertyPathNode = new XamlIlPropertyPathNode(text, elements, types); if (propertyPathNode.Type == null) { throw new XamlX.XamlParseException("Unexpected end of the property path", text); } pv.Values[0] = propertyPathNode; } 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); }
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); var avaloniaAttachedPropertyT = context.GetAvaloniaTypes().AvaloniaAttachedPropertyT; 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, type: 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.AttachedPropertySyntax attachedProperty: { var targetType = result?.TargetType; if (targetType == null) { throw new XamlParseException("Attached Property selectors must be applied to a type.", node); } var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type; if (attachedPropertyOwnerType is null) { throw new XamlParseException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}", node); } var attachedPropertyName = attachedProperty.Property + "Property"; var targetPropertyField = attachedPropertyOwnerType.GetAllFields() .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == attachedPropertyName && f.FieldType.GenericTypeDefinition == avaloniaAttachedPropertyT ); if (targetPropertyField is null) { throw new XamlParseException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType.GetFqn()}", node); } var targetPropertyType = XamlIlAvaloniaPropertyHelper .GetAvaloniaPropertyType(targetPropertyField, context.GetAvaloniaTypes(), node); if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, new XamlAstTextNode(node, attachedProperty.Value, type: context.Configuration.WellKnownTypes.String), targetPropertyType, out var typedValue)) { throw new XamlParseException( $"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}", node); } result = new XamlIlAttacchedPropertyEqualsSelector(result, targetPropertyField, 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)); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) { return(node); } var parent = context.ParentNodes().OfType <XamlAstObjectNode>() .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style"); if (parent == null) { throw new XamlParseException( "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node); } var selectorProperty = parent.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector"); if (selectorProperty == null) { throw new XamlParseException( "Can not find parent Style Selector", node); } var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode; if (selector?.TargetType == null) { throw new XamlParseException( "Can not resolve parent Style Selector type", node); } IXamlType propType = null; var property = @on.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property"); if (property != null) { var propertyName = property.Values.OfType <XamlAstTextNode>().FirstOrDefault()?.Text; if (propertyName == null) { throw new XamlParseException("Setter.Property must be a string", node); } var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, new XamlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]); property.Values = new List <IXamlAstValueNode> { avaloniaPropertyNode }; propType = avaloniaPropertyNode.AvaloniaPropertyType; } else { var propertyPath = on.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath"); if (propertyPath == null) { throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node); } if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn && ppn.PropertyType != null) { propType = ppn.PropertyType; }