2016-03-22 23:02:25 +03:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Xml;
|
|
|
|
using Xamarin.Forms.Internals;
|
|
|
|
|
|
|
|
namespace Xamarin.Forms.Xaml
|
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
interface INode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
List<string> IgnorablePrefixes { get; set; }
|
|
|
|
IXmlNamespaceResolver NamespaceResolver { get; }
|
|
|
|
INode Parent { get; set; }
|
|
|
|
|
|
|
|
void Accept(IXamlNodeVisitor visitor, INode parentNode);
|
2016-08-16 21:33:44 +03:00
|
|
|
INode Clone();
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
interface IValueNode : INode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
interface IElementNode : INode, IListNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
Dictionary<XmlName, INode> Properties { get; }
|
|
|
|
List<XmlName> SkipProperties { get; }
|
2019-09-03 23:45:22 +03:00
|
|
|
NameScopeRef NameScopeRef { get; }
|
2016-03-22 23:02:25 +03:00
|
|
|
XmlType XmlType { get; }
|
|
|
|
string NamespaceURI { get; }
|
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
interface IListNode : INode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
List<INode> CollectionItems { get; }
|
|
|
|
}
|
|
|
|
|
2019-09-03 23:45:22 +03:00
|
|
|
class NameScopeRef
|
|
|
|
{
|
|
|
|
public INameScope NameScope { get; set; }
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
[DebuggerDisplay("{NamespaceUri}:{Name}")]
|
2016-11-15 22:39:48 +03:00
|
|
|
class XmlType
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
public XmlType(string namespaceUri, string name, IList<XmlType> typeArguments)
|
|
|
|
{
|
|
|
|
NamespaceUri = namespaceUri;
|
|
|
|
Name = name;
|
|
|
|
TypeArguments = typeArguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
public string NamespaceUri { get; }
|
|
|
|
public string Name { get; }
|
2016-11-15 22:39:48 +03:00
|
|
|
public IList<XmlType> TypeArguments { get; }
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
abstract class BaseNode : IXmlLineInfo, INode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
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<string> IgnorablePrefixes { get; set; }
|
|
|
|
public int LineNumber { get; set; }
|
|
|
|
public int LinePosition { get; set; }
|
2016-08-16 21:33:44 +03:00
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
public bool HasLineInfo() => LineNumber >= 0 && LinePosition >= 0;
|
|
|
|
|
|
|
|
public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode);
|
2016-08-16 21:33:44 +03:00
|
|
|
public abstract INode Clone();
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
[DebuggerDisplay("{Value}")]
|
2017-01-25 16:47:27 +03:00
|
|
|
class ValueNode : BaseNode, IValueNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2016-08-16 21:33:44 +03:00
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
public override INode Clone() => new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) {
|
|
|
|
IgnorablePrefixes = IgnorablePrefixes
|
|
|
|
};
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
[DebuggerDisplay("{MarkupString}")]
|
2017-01-25 16:47:27 +03:00
|
|
|
class MarkupNode : BaseNode, IValueNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
2016-03-22 23:02:25 +03:00
|
|
|
: base(namespaceResolver, linenumber, lineposition)
|
|
|
|
{
|
|
|
|
MarkupString = markupString;
|
|
|
|
}
|
|
|
|
|
|
|
|
public string MarkupString { get; }
|
|
|
|
|
|
|
|
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
|
|
|
{
|
|
|
|
visitor.Visit(this, parentNode);
|
|
|
|
}
|
2016-08-16 21:33:44 +03:00
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
public override INode Clone() => new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) {
|
|
|
|
IgnorablePrefixes = IgnorablePrefixes
|
|
|
|
};
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2019-09-03 23:45:22 +03:00
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
[DebuggerDisplay("{XmlType.Name}")]
|
|
|
|
class ElementNode : BaseNode, IValueNode, IElementNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
public ElementNode(XmlType type, string namespaceURI, IXmlNamespaceResolver namespaceResolver, int linenumber = -1,
|
|
|
|
int lineposition = -1)
|
|
|
|
: base(namespaceResolver, linenumber, lineposition)
|
|
|
|
{
|
|
|
|
Properties = new Dictionary<XmlName, INode>();
|
|
|
|
SkipProperties = new List<XmlName>();
|
|
|
|
CollectionItems = new List<INode>();
|
|
|
|
XmlType = type;
|
|
|
|
NamespaceURI = namespaceURI;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Dictionary<XmlName, INode> Properties { get; }
|
|
|
|
public List<XmlName> SkipProperties { get; }
|
|
|
|
public List<INode> CollectionItems { get; }
|
|
|
|
public XmlType XmlType { get; }
|
|
|
|
public string NamespaceURI { get; }
|
2019-09-03 23:45:22 +03:00
|
|
|
public NameScopeRef NameScopeRef { get; set; }
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
2017-01-25 16:47:27 +03:00
|
|
|
|
2018-01-11 22:03:12 +03:00
|
|
|
if (!SkipChildren(visitor, this, parentNode)) {
|
2016-03-22 23:02:25 +03:00
|
|
|
foreach (var node in Properties.Values.ToList())
|
|
|
|
node.Accept(visitor, this);
|
|
|
|
foreach (var node in CollectionItems)
|
|
|
|
node.Accept(visitor, this);
|
|
|
|
}
|
2017-01-25 16:47:27 +03:00
|
|
|
|
|
|
|
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
2017-01-25 16:47:27 +03:00
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
bool IsDataTemplate(INode parentNode)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2019-09-03 23:45:22 +03:00
|
|
|
if (parentNode is IElementNode parentElement &&
|
|
|
|
parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) &&
|
2017-01-25 16:47:27 +03:00
|
|
|
createContent == this)
|
2016-03-22 23:02:25 +03:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-11 22:03:12 +03:00
|
|
|
protected bool SkipChildren(IXamlNodeVisitor visitor, INode node, INode parentNode) =>
|
|
|
|
(visitor.StopOnDataTemplate && IsDataTemplate(parentNode))
|
2018-04-20 06:50:47 +03:00
|
|
|
|| (visitor.StopOnResourceDictionary && visitor.IsResourceDictionary(this))
|
2018-01-11 22:03:12 +03:00
|
|
|
|| visitor.SkipChildren(node, parentNode);
|
2017-01-25 16:47:27 +03:00
|
|
|
|
|
|
|
protected bool SkipVisitNode(IXamlNodeVisitor visitor, INode parentNode) =>
|
|
|
|
!visitor.VisitNodeOnDataTemplate && IsDataTemplate(parentNode);
|
2016-08-16 21:33:44 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
abstract class RootNode : ElementNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2019-11-26 03:29:40 +03:00
|
|
|
protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver, int linenumber = -1, int lineposition = -1) : base(xmlType, xmlType.NamespaceUri, nsResolver, linenumber: linenumber, lineposition: lineposition)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
2017-01-25 16:47:27 +03:00
|
|
|
|
2018-01-11 22:03:12 +03:00
|
|
|
if (!SkipChildren(visitor, this, parentNode)) {
|
2016-04-18 00:59:44 +03:00
|
|
|
foreach (var node in Properties.Values.ToList())
|
|
|
|
node.Accept(visitor, this);
|
|
|
|
foreach (var node in CollectionItems)
|
|
|
|
node.Accept(visitor, this);
|
|
|
|
}
|
2017-01-25 16:47:27 +03:00
|
|
|
|
|
|
|
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
class ListNode : BaseNode, IListNode, IValueNode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
|
|
|
: base(namespaceResolver, linenumber, lineposition)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
CollectionItems = nodes.ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlName XmlName { get; set; }
|
|
|
|
public List<INode> CollectionItems { get; set; }
|
|
|
|
|
|
|
|
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
if (visitor.VisitingMode == TreeVisitingMode.TopDown)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
|
|
|
foreach (var node in CollectionItems)
|
|
|
|
node.Accept(visitor, this);
|
2017-01-25 16:47:27 +03:00
|
|
|
if (visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
2016-03-22 23:02:25 +03:00
|
|
|
visitor.Visit(this, parentNode);
|
|
|
|
}
|
2016-08-16 21:33:44 +03:00
|
|
|
|
|
|
|
public override INode Clone()
|
|
|
|
{
|
|
|
|
var items = new List<INode>();
|
|
|
|
foreach (var p in CollectionItems)
|
|
|
|
items.Add(p.Clone());
|
|
|
|
return new ListNode(items, NamespaceResolver, LineNumber, LinePosition) {
|
|
|
|
IgnorablePrefixes = IgnorablePrefixes
|
|
|
|
};
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 16:47:27 +03:00
|
|
|
static class INodeExtensions
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
public static bool SkipPrefix(this INode node, string prefix)
|
|
|
|
{
|
2017-01-25 16:47:27 +03:00
|
|
|
do {
|
2016-03-22 23:02:25 +03:00
|
|
|
if (node.IgnorablePrefixes != null && node.IgnorablePrefixes.Contains(prefix))
|
|
|
|
return true;
|
|
|
|
node = node.Parent;
|
|
|
|
} while (node != null);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-04-20 06:50:47 +03:00
|
|
|
}
|