Esempio n. 1
0
        private static XamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func <IXamlType> startTypeResolver, IXamlType selfType, IEnumerable <BindingExpressionGrammar.INode> bindingExpression)
        {
            List <IXamlIlBindingPathElementNode> transformNodes = new List <IXamlIlBindingPathElementNode>();
            List <IXamlIlBindingPathElementNode> nodes          = new List <IXamlIlBindingPathElementNode>();

            foreach (var astNode in bindingExpression)
            {
                var targetTypeResolver = nodes.Count == 0 ? startTypeResolver : () => 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 targetType = targetTypeResolver();
                    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:
                {
                    IXamlType targetType = targetTypeResolver();
                    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 if (GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName) is IXamlProperty clrProperty)
                    {
                        nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty));
                    }
                    else if (GetAllDefinedMethods(targetType).FirstOrDefault(m => m.Name == propName.PropertyName) is IXamlMethod method)
                    {
                        nodes.Add(new XamlIlClrMethodPathElementNode(method, context.Configuration.WellKnownTypes.Delegate));
                    }
                    else
                    {
                        throw new XamlX.XamlParseException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
                    }
                    break;
                }

                case BindingExpressionGrammar.IndexerNode indexer:
                {
                    IXamlType targetType = targetTypeResolver();
                    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(selfType));
                    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;