using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Xml; using Xamarin.Forms.Internals; namespace Xamarin.Forms.Xaml { interface INode { List IgnorablePrefixes { get; set; } IXmlNamespaceResolver NamespaceResolver { get; } INode Parent { get; set; } void Accept(IXamlNodeVisitor visitor, INode parentNode); INode Clone(); } interface IValueNode : INode { } interface IElementNode : INode, IListNode { Dictionary Properties { get; } List SkipProperties { get; } INameScope Namescope { get; } XmlType XmlType { get; } string NamespaceURI { get; } } interface IListNode : INode { List CollectionItems { get; } } [DebuggerDisplay("{NamespaceUri}:{Name}")] class XmlType { public XmlType(string namespaceUri, string name, IList typeArguments) { NamespaceUri = namespaceUri; Name = name; TypeArguments = typeArguments; } public string NamespaceUri { get; } public string Name { get; } public IList TypeArguments { get; } } abstract class BaseNode : IXmlLineInfo, INode { protected BaseNode(IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) { NamespaceResolver = namespaceResolver; LineNumber = linenumber; LinePosition = lineposition; } public IXmlNamespaceResolver NamespaceResolver { get; } public INode Parent { get; set; } public List IgnorablePrefixes { get; set; } public int LineNumber { get; set; } public int LinePosition { get; set; } public bool HasLineInfo() => LineNumber >= 0 && LinePosition >= 0; public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode); public abstract INode Clone(); } [DebuggerDisplay("{Value}")] class ValueNode : BaseNode, IValueNode { public ValueNode(object value, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) { Value = value; } public object Value { get; set; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { visitor.Visit(this, parentNode); } public override INode Clone() => new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) { IgnorablePrefixes = IgnorablePrefixes }; } [DebuggerDisplay("{MarkupString}")] class MarkupNode : BaseNode, IValueNode { public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) { MarkupString = markupString; } public string MarkupString { get; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { visitor.Visit(this, parentNode); } public override INode Clone() => new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) { IgnorablePrefixes = IgnorablePrefixes }; } [DebuggerDisplay("{XmlType.Name}")] class ElementNode : BaseNode, IValueNode, IElementNode { public ElementNode(XmlType type, string namespaceURI, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) { Properties = new Dictionary(); SkipProperties = new List(); CollectionItems = new List(); XmlType = type; NamespaceURI = namespaceURI; } public Dictionary Properties { get; } public List SkipProperties { get; } public List CollectionItems { get; } public XmlType XmlType { get; } public string NamespaceURI { get; } public INameScope Namescope { get; set; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); if (!SkipChildren(visitor, parentNode)) { foreach (var node in Properties.Values.ToList()) node.Accept(visitor, this); foreach (var node in CollectionItems) node.Accept(visitor, this); } if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); } bool IsDataTemplate(INode parentNode) { var parentElement = parentNode as IElementNode; INode createContent; if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) && createContent == this) return true; return false; } bool IsResourceDictionary() => XmlType.Name == "ResourceDictionary"; protected bool SkipChildren(IXamlNodeVisitor visitor, INode parentNode) => (visitor.StopOnDataTemplate && IsDataTemplate(parentNode)) || (visitor.StopOnResourceDictionary && IsResourceDictionary()); protected bool SkipVisitNode(IXamlNodeVisitor visitor, INode parentNode) => !visitor.VisitNodeOnDataTemplate && IsDataTemplate(parentNode); public override INode Clone() { var clone = new ElementNode(XmlType, NamespaceURI, NamespaceResolver, LineNumber, LinePosition) { IgnorablePrefixes = IgnorablePrefixes }; foreach (var kvp in Properties) clone.Properties.Add(kvp.Key, kvp.Value.Clone()); foreach (var p in SkipProperties) clone.SkipProperties.Add(p); foreach (var p in CollectionItems) clone.CollectionItems.Add(p.Clone()); return clone; } } abstract class RootNode : ElementNode { protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver) : base(xmlType, xmlType.NamespaceUri, nsResolver) { } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); if (!SkipChildren(visitor, parentNode)) { foreach (var node in Properties.Values.ToList()) node.Accept(visitor, this); foreach (var node in CollectionItems) node.Accept(visitor, this); } if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); } } class ListNode : BaseNode, IListNode, IValueNode { public ListNode(IList nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) { CollectionItems = nodes.ToList(); } public XmlName XmlName { get; set; } public List CollectionItems { get; set; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { if (visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); foreach (var node in CollectionItems) node.Accept(visitor, this); if (visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); } public override INode Clone() { var items = new List(); foreach (var p in CollectionItems) items.Add(p.Clone()); return new ListNode(items, NamespaceResolver, LineNumber, LinePosition) { IgnorablePrefixes = IgnorablePrefixes }; } } static class INodeExtensions { public static bool SkipPrefix(this INode node, string prefix) { do { if (node.IgnorablePrefixes != null && node.IgnorablePrefixes.Contains(prefix)) return true; node = node.Parent; } while (node != null); return false; } } }