Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        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);
        }