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 = GetAllDefinedProperties(targetType).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; foreach (var currentType in TraverseTypeHierarchy(targetType)) { var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute"); if (defaultMemberAttribute != null) { property = currentType.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())) .Skip(1) .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 XamlAstConstructableObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) { IXamlType startType = null; var sourceProperty = binding.Children.OfType <XamlPropertyAssignmentNode>().FirstOrDefault(c => c.Property.Name == "Source"); var dataTypeProperty = binding.Children.OfType <XamlPropertyAssignmentNode>().FirstOrDefault(c => c.Property.Name == "DataType"); if (sourceProperty?.Values.Count is 1) { var sourceValue = sourceProperty.Values[0]; switch (sourceValue) { case XamlAstTextNode textNode: startType = textNode.Type?.GetClrType(); break; case XamlMarkupExtensionNode extension: startType = extension.Type?.GetClrType(); //let's try to infer StaticResource type from parent resources in xaml if (extension.Value.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.MarkupExtensions.StaticResourceExtension" && extension.Value is XamlAstConstructableObjectNode cn && cn.Arguments.Count == 1 && cn.Arguments[0] is XamlAstTextNode keyNode) { bool matchProperty(IXamlAstNode node, IXamlType styledElementType, string propertyName) { return((node is XamlPropertyAssignmentNode p && p.Property.DeclaringType == styledElementType && p.Property.Name == propertyName) || (node is XamlManipulationGroupNode m && m.Children.Count > 0 && m.Children[0] is XamlPropertyAssignmentNode pm && pm.Property.DeclaringType == styledElementType && pm.Property.Name == propertyName)); } string getResourceValue_xKey(XamlPropertyAssignmentNode node) => node.Values.Count == 2 && node.Values[0] is XamlAstTextNode t ? t.Text : ""; IXamlType getResourceValue_Type(XamlPropertyAssignmentNode node, IXamlType xamlType) => node.Values.Count == 2 ? node.Values[1].Type.GetClrType() : xamlType; IEnumerable <XamlPropertyAssignmentNode> getResourceValues(IXamlAstNode node) { if (node is XamlPropertyAssignmentNode propertyNode) { if (propertyNode.Values.Count == 1 && propertyNode.Values[0] is XamlAstConstructableObjectNode obj && obj.Type.GetClrType().FullName == "Avalonia.Controls.ResourceDictionary") { foreach (var r in obj.Children.SelectMany(c => getResourceValues(c))) { yield return(r); } } else { yield return(propertyNode); } } else if (node is XamlManipulationGroupNode m) { foreach (var r in m.Children.OfType <XamlPropertyAssignmentNode>()) { yield return(r); } } } string key = keyNode.Text; var styledElement = context.GetAvaloniaTypes().StyledElement; var resource = context.ParentNodes() .OfType <XamlAstConstructableObjectNode>() .Where(o => styledElement.IsAssignableFrom(o.Type.GetClrType())) .Select(o => o.Children.FirstOrDefault(p => matchProperty(p, styledElement, "Resources"))) .Where(r => r != null) .SelectMany(r => getResourceValues(r)) .FirstOrDefault(r => getResourceValue_xKey(r) == key); if (resource != null) { startType = getResourceValue_Type(resource, startType); } }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlDataContextTypeMetadataNode) { // We've already resolved the data context type for this node. return(node); } if (node is XamlAstConstructableObjectNode on) { AvaloniaXamlIlDataContextTypeMetadataNode inferredDataContextTypeNode = null; AvaloniaXamlIlDataContextTypeMetadataNode directiveDataContextTypeNode = null; for (int i = 0; i < on.Children.Count; ++i) { var child = on.Children[i]; if (child is XamlAstXmlDirective directive) { if (directive.Namespace == XamlNamespaces.Xaml2006 && directive.Name == "DataType" && directive.Values.Count == 1) { on.Children.RemoveAt(i); i--; if (directive.Values[0] is XamlAstTextNode text) { directiveDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, TypeReferenceResolver.ResolveType(context, text.Text, isMarkupExtension: false, text, strict: true).Type); } else { throw new XamlX.XamlParseException("x:DataType should be set to a type name.", directive.Values[0]); } } } else if (child is XamlPropertyAssignmentNode pa) { var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; if (pa.Property.Name == "DataContext" && pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) && pa.Values[0] is XamlMarkupExtensionNode ext && ext.Value is XamlAstConstructableObjectNode obj) { inferredDataContextTypeNode = ParseDataContext(context, on, obj); } else if (pa.Property.CustomAttributes.Any(a => a.Type == templateDataTypeAttribute) && pa.Values[0] is XamlTypeExtensionNode dataTypeNode) { inferredDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, dataTypeNode.Value.GetClrType()); } } } // If there is no x:DataType directive, // do more specialized inference if (directiveDataContextTypeNode is null) { if (context.GetAvaloniaTypes().IDataTemplate.IsAssignableFrom(on.Type.GetClrType()) && inferredDataContextTypeNode is null) { // Infer data type from collection binding on a control that displays items. var parentObject = context.ParentNodes().OfType <XamlAstConstructableObjectNode>().FirstOrDefault(); if (parentObject != null) { var parentType = parentObject.Type.GetClrType(); if (context.GetAvaloniaTypes().IItemsPresenterHost.IsDirectlyAssignableFrom(parentType) || context.GetAvaloniaTypes().ItemsRepeater.IsDirectlyAssignableFrom(parentType)) { inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject); } } if (inferredDataContextTypeNode is null) { inferredDataContextTypeNode = new AvaloniaXamlIlUninferrableDataContextMetadataNode(on); } } } return(directiveDataContextTypeNode ?? inferredDataContextTypeNode ?? node); }
private static BindingExpressionGrammar.INode ConvertLongFormPropertiesToBindingExpressionNode( AstTransformationContext context, XamlAstObjectNode binding) { BindingExpressionGrammar.INode convertedNode = null; var syntheticCompiledBindingProperties = binding.Children.OfType <XamlAstXamlPropertyValueNode>() .Where(v => v.Property is AvaloniaSyntheticCompiledBindingProperty) .ToList(); var elementNameProperty = syntheticCompiledBindingProperties .FirstOrDefault(v => v.Property is AvaloniaSyntheticCompiledBindingProperty prop && prop.Name == SyntheticCompiledBindingPropertyName.ElementName); var relativeSourceProperty = syntheticCompiledBindingProperties .FirstOrDefault(v => v.Property is AvaloniaSyntheticCompiledBindingProperty prop && prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource); var sourceProperty = binding.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(v => v.Property is XamlAstClrProperty prop && prop.Name == "Source"); if (elementNameProperty?.Values[0] is XamlAstTextNode elementName) { convertedNode = new BindingExpressionGrammar.NameNode { Name = elementName.Text }; } else if (elementNameProperty != null) { throw new XamlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]); } if (sourceProperty != null && convertedNode != null) { throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding); } if (GetRelativeSourceObjectFromAssignment( context, relativeSourceProperty, out var relativeSourceObject)) { if (convertedNode != null) { throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding); } var mode = relativeSourceObject.Children .OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Mode") ?.Values[0] is XamlAstTextNode modeAssignedValue ? modeAssignedValue.Text : null; if (relativeSourceObject.Arguments.Count == 0 && mode == null) { mode = "FindAncestor"; } if (mode == "FindAncestor") { var ancestorLevel = relativeSourceObject.Children .OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "FindAncestor") ?.Values[0] is XamlAstTextNode ancestorLevelText?int.Parse(ancestorLevelText.Text) - 1 : 0; var treeType = relativeSourceObject.Children .OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Tree") ?.Values[0] is XamlAstTextNode treeTypeValue ? treeTypeValue.Text : "Visual"; var ancestorTypeName = relativeSourceObject.Children .OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(x => x.Property.GetClrProperty().Name == "AncestorType") ?.Values[0] as XamlAstTextNode; IXamlType ancestorType = null; if (ancestorTypeName is null) { if (treeType == "Visual") { throw new XamlParseException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.", relativeSourceObject); } else if (treeType == "Logical") { var styledElementType = context.GetAvaloniaTypes().StyledElement; ancestorType = context .ParentNodes() .OfType <XamlAstObjectNode>() .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType())) .ElementAtOrDefault(ancestorLevel) ?.Type.GetClrType(); if (ancestorType is null) { throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", relativeSourceObject); } } } else { ancestorType = TypeReferenceResolver.ResolveType( context, ancestorTypeName.Text, false, ancestorTypeName, true).GetClrType(); } if (treeType == "Visual") { convertedNode = new VisualAncestorBindingExpressionNode { Type = ancestorType, Level = ancestorLevel }; } else if (treeType == "Logical") { convertedNode = new LogicalAncestorBindingExpressionNode { Type = ancestorType, Level = ancestorLevel }; } else { throw new XamlParseException($"Unknown tree type '{treeType}'.", binding); } } else if (mode == "DataContext") { convertedNode = null; } else if (mode == "Self") { convertedNode = new BindingExpressionGrammar.SelfNode(); } else if (mode == "TemplatedParent") { var parentType = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>() .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate) ?.TargetType.GetClrType(); if (parentType is null) { throw new XamlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding); } convertedNode = new TemplatedParentBindingExpressionNode { Type = parentType }; } else { throw new XamlParseException($"Unknown RelativeSource mode '{mode}'.", binding); } } if (elementNameProperty != null) { binding.Children.Remove(elementNameProperty); } if (relativeSourceProperty != null) { binding.Children.Remove(relativeSourceProperty); } return(convertedNode); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) { var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding); if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText) { var reader = new CharacterReader(bindingPathText.Text.AsSpan()); var(nodes, _) = BindingExpressionGrammar.Parse(ref reader); if (convertedNode != null) { nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); } binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); } else { var bindingPathAssignment = binding.Children.OfType <XamlAstXamlPropertyValueNode>() .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); if (bindingPathAssignment != null && bindingPathAssignment.Values[0] is XamlAstTextNode pathValue) { var reader = new CharacterReader(pathValue.Text.AsSpan()); var(nodes, _) = BindingExpressionGrammar.Parse(ref reader); if (convertedNode != null) { nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); } bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes); } } } return(node); }
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 static bool CustomValueConverter(AstTransformationContext context, IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result) { if (!(node is XamlAstTextNode textNode)) { result = null; return(false); } var text = textNode.Text; var types = context.GetAvaloniaTypes(); if (type.FullName == "System.TimeSpan") { var tsText = text.Trim(); if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) { // // shorthand seconds format (ie. "0.25") if (!tsText.Contains(":") && double.TryParse(tsText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var seconds)) { timeSpan = TimeSpan.FromSeconds(seconds); } else { throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); } } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.FindMethod("FromTicks", type, false, types.Long), new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); return(true); } if (type.Equals(types.FontFamily)) { result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return(true); } if (type.Equals(types.Thickness)) { try { var thickness = Thickness.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); } } if (type.Equals(types.Point)) { try { var point = Point.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, new[] { point.X, point.Y }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); } } if (type.Equals(types.Vector)) { try { var vector = Vector.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, new[] { vector.X, vector.Y }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); } } if (type.Equals(types.Size)) { try { var size = Size.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, new[] { size.Width, size.Height }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); } } if (type.Equals(types.Matrix)) { try { var matrix = Matrix.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); } } if (type.Equals(types.CornerRadius)) { try { var cornerRadius = CornerRadius.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); } } if (type.Equals(types.Color)) { if (!Color.TryParse(text, out Color color)) { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.GetMethod( new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); return(true); } if (type.Equals(types.GridLength)) { try { var gridLength = GridLength.Parse(text); result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); } } if (type.Equals(types.Cursor)) { if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) { var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List <IXamlAstValueNode> { enumConstantNode }); return(true); } } if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault(); if (scope == null) { throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); } result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node); return(true); } result = null; return(false); }
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)); }