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 XamlAstXamlPropertyValueNode propertyNode) { var childNodes = propertyNode.Values; WhitespaceNormalization.Apply( childNodes, context.Configuration ); var property = propertyNode.Property.GetClrProperty(); if (!WantsWhitespaceOnlyElements(context.Configuration, property)) { WhitespaceNormalization.RemoveWhitespaceNodes(childNodes); } } return(node); }
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { if (node is XamlAstObjectNode ni) { var argDirectives = ni.Children.OfType <XamlAstObjectNode>() .Where(d => d.Type is XamlAstXmlTypeReference xref && xref.XmlNamespace == XamlNamespaces.Xaml2006 && xref.Name == "Arguments").ToList(); if (argDirectives.Count > 1 && context.StrictMode) { throw new XamlParseException("x:Arguments directive is specified more than once", argDirectives[1]); } if (argDirectives.Count == 0) { return(node); } ni.Arguments = argDirectives[0].Children.OfType <IXamlAstValueNode>().ToList(); ni.Children.Remove(argDirectives[0]); // This is needed to remove whitespace-only nodes between actual object elements or text nodes WhitespaceNormalization.RemoveWhitespaceNodes(ni.Arguments); } return(node); }
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); }