static void Check(string s, params PropertyPathGrammar.ISyntax[] expected) { var parsed = PropertyPathGrammar.Parse(s).ToList(); Assert.Equal(expected.Length, parsed.Count); for (var c = 0; c < parsed.Count; c++) { Assert.Equal(expected[c], parsed[c]); } }
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); }