diff options
author | Stephane Delcroix <stephane@delcroix.org> | 2017-01-25 14:47:27 +0100 |
---|---|---|
committer | Kangho Hur <kangho.hur@samsung.com> | 2017-03-24 13:14:23 +0900 |
commit | c247c695cbd8613d5603ccbada92051a9ea7dbf8 (patch) | |
tree | 61ffc2f8687576377ebe71c73d74001589b9f2af | |
parent | acf4a77b5a71900463f1e31cad1dab5e9593451e (diff) | |
download | xamarin-forms-c247c695cbd8613d5603ccbada92051a9ea7dbf8.tar.gz xamarin-forms-c247c695cbd8613d5603ccbada92051a9ea7dbf8.tar.bz2 xamarin-forms-c247c695cbd8613d5603ccbada92051a9ea7dbf8.zip |
[Xaml[C]] Do not instantiate DataTemplate Content at parsing time (#683)
* [Xaml] rename VisitChildrenFirst
* [Xaml] rework SkipChildren in XamlNode
* [Xaml] fix 45179
* fix
20 files changed, 229 insertions, 258 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs index c684e512..b73926fd 100644 --- a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs @@ -21,20 +21,11 @@ namespace Xamarin.Forms.Build.Tasks ModuleDefinition Module { get; } - public bool VisitChildrenFirst - { - get { return true; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs index e647f6c2..ff83de7a 100644 --- a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs @@ -24,20 +24,10 @@ namespace Xamarin.Forms.Build.Tasks ILContext Context { get; } - public bool VisitChildrenFirst - { - get { return true; } - } - - public bool StopOnDataTemplate - { - get { return false; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => false; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => true; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs b/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs index 1839cf7b..9a5b7444 100644 --- a/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs @@ -13,20 +13,10 @@ namespace Xamarin.Forms.Build.Tasks public ILContext Context { get; } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs index d810bd80..1c776ab8 100644 --- a/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs @@ -16,20 +16,10 @@ namespace Xamarin.Forms.Build.Tasks ILContext Context { get; } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs index c93c14f2..2c4ddbd6 100644 --- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs @@ -37,8 +37,9 @@ namespace Xamarin.Forms.Build.Tasks public ILContext Context { get; } public bool StopOnResourceDictionary { get; } - public bool VisitChildrenFirst { get; } = true; - public bool StopOnDataTemplate { get; } = true; + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => true; + public bool VisitNodeOnDataTemplate => true; ModuleDefinition Module { get; } @@ -88,6 +89,11 @@ namespace Xamarin.Forms.Build.Tasks if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName)) return; + if (propertyName == XmlName._CreateContent) { + SetDataTemplate((IElementNode)parentNode, node, Context, node); + return; + } + //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable var vardef = Context.Variables[node]; var vardefref = new VariableDefinitionReference(vardef); @@ -114,11 +120,8 @@ namespace Xamarin.Forms.Build.Tasks return; if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName)) return; - - if (propertyName == XmlName._CreateContent) - SetDataTemplate((IElementNode)parentNode, node, Context, node); - else - Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node)); + + Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node)); } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) { diff --git a/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs index f046efcb..c5be1fbd 100644 --- a/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; @@ -20,26 +19,15 @@ namespace Xamarin.Forms.Build.Tasks ModuleDefinition Module { get; } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { XmlName propertyName; - if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName)) - { + if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName)) { if (!IsCollectionItem(node, parentNode)) return; string contentProperty; diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27863.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27863.xaml.cs index 03ebaaf9..1120a033 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27863.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27863.xaml.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Xamarin.Forms; using NUnit.Framework; +using Xamarin.Forms.Core.UnitTests; namespace Xamarin.Forms.Xaml.UnitTests { @@ -22,6 +23,18 @@ namespace Xamarin.Forms.Xaml.UnitTests [TestFixture] class Tests { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices(); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + [TestCase(true)] [TestCase(false)] public void DataTemplateInResourceDictionaries (bool useCompiledXaml) diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27968.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27968.xaml.cs index 2482e9f0..507073d7 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27968.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz27968.xaml.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Xamarin.Forms; using NUnit.Framework; +using Xamarin.Forms.Core.UnitTests; namespace Xamarin.Forms.Xaml.UnitTests { @@ -26,6 +27,18 @@ namespace Xamarin.Forms.Xaml.UnitTests [TestFixture] class Tests { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices(); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + [TestCase(true)] [TestCase(false)] public void BaseClassIdentifiersAreValidForResources (bool useCompiledXaml) diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml new file mode 100644 index 00000000..a89aa295 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentPage + xmlns="http://xamarin.com/schemas/2014/forms" + xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" + xmlns:local="clr-namespace:Xamarin.Forms.Xaml.UnitTests" + x:Class="Xamarin.Forms.Xaml.UnitTests.Bz45179"> + <ContentPage.Resources> + <ResourceDictionary> + <DataTemplate x:Key="dt0"> + <local:Bz45179_0/> + </DataTemplate> + </ResourceDictionary> + </ContentPage.Resources> +</ContentPage>
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml.cs new file mode 100644 index 00000000..1dbdb44d --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz45179.xaml.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Xamarin.Forms; +using Xamarin.Forms.Core.UnitTests; + +namespace Xamarin.Forms.Xaml.UnitTests +{ + public class Bz45179_0 : ContentView { + public static int creator_count; + public Bz45179_0() + { + creator_count++; + } + + } + public partial class Bz45179 : ContentPage + { + public Bz45179() + { + InitializeComponent(); + } + + public Bz45179(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [TestFixture] + class Tests + { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices(); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + + [TestCase(true)] + [TestCase(false)] + public void DTDoNotInstantiateTheirContent(bool useCompiledXaml) + { + Bz45179_0.creator_count = 0; + Assume.That(Bz45179_0.creator_count, Is.EqualTo(0)); + var page = new Bz45179(useCompiledXaml); + Assert.That(Bz45179_0.creator_count, Is.EqualTo(0)); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs index 154ba023..879ea3b0 100644 --- a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs +++ b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs @@ -35,15 +35,10 @@ namespace Xamarin.Forms.Xaml HydratationContext Context { get; } - public bool VisitChildrenFirst { - get { return true; } - } - - public bool StopOnDataTemplate { - get { return true; } - } - + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => true; public bool StopOnResourceDictionary { get; } + public bool VisitNodeOnDataTemplate => true; public void Visit(ValueNode node, INode parentNode) { @@ -81,6 +76,15 @@ namespace Xamarin.Forms.Xaml public void Visit(ElementNode node, INode parentNode) { + var propertyName = XmlName.Empty; + if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent){ + var s0 = Values[parentNode]; + if (s0 is ElementTemplate) { + SetTemplate(s0 as ElementTemplate, node); + return; + } + } + var value = Values [node]; var parentElement = parentNode as IElementNode; var markupExtension = value as IMarkupExtension; @@ -96,7 +100,7 @@ namespace Xamarin.Forms.Xaml value = valueProvider.ProvideValue(serviceProvider); } - XmlName propertyName = XmlName.Empty; + propertyName = XmlName.Empty; //Simplify ListNodes with single elements var pList = parentNode as ListNode; @@ -113,11 +117,7 @@ namespace Xamarin.Forms.Xaml return; var source = Values [parentNode]; - - if (propertyName == XmlName._CreateContent && source is ElementTemplate) - SetTemplate(source as ElementTemplate, node); - else - SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node); + SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node); } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) { // Collection element, implicit content, or implicit collection element. string contentProperty; diff --git a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs index 817b62c8..b4f9213a 100644 --- a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs +++ b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs @@ -23,20 +23,10 @@ namespace Xamarin.Forms.Xaml HydratationContext Context { get; } - public bool VisitChildrenFirst - { - get { return true; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs index 81893506..282b7da4 100644 --- a/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs +++ b/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs @@ -28,20 +28,10 @@ namespace Xamarin.Forms.Xaml HydratationContext Context { get; } - public bool VisitChildrenFirst - { - get { return true; } - } - - public bool StopOnDataTemplate - { - get { return false; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; + public bool StopOnDataTemplate => false; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => true; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/FillResourceDictionariesVisitor.cs b/Xamarin.Forms.Xaml/FillResourceDictionariesVisitor.cs index a3f12109..509d54e8 100644 --- a/Xamarin.Forms.Xaml/FillResourceDictionariesVisitor.cs +++ b/Xamarin.Forms.Xaml/FillResourceDictionariesVisitor.cs @@ -20,20 +20,10 @@ namespace Xamarin.Forms.Xaml get { return Context.Values; } } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs b/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs index 703fc206..34f1c569 100644 --- a/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -34,7 +34,7 @@ namespace Xamarin.Forms.Xaml } if (resource == null && (Application.Current == null || Application.Current.Resources == null || !Application.Current.Resources.TryGetMergedValue(Key, out resource))) - throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo); + throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo); var bp = valueProvider.TargetProperty as BindableProperty; var pi = valueProvider.TargetProperty as PropertyInfo; diff --git a/Xamarin.Forms.Xaml/NamescopingVisitor.cs b/Xamarin.Forms.Xaml/NamescopingVisitor.cs index 0651d045..ec9b9851 100644 --- a/Xamarin.Forms.Xaml/NamescopingVisitor.cs +++ b/Xamarin.Forms.Xaml/NamescopingVisitor.cs @@ -14,20 +14,10 @@ namespace Xamarin.Forms.Xaml Dictionary<INode, object> Values { get; set; } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return false; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => false; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => true; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/PruneIgnoredNodesVisitor.cs b/Xamarin.Forms.Xaml/PruneIgnoredNodesVisitor.cs index 95ddb20f..92badecd 100644 --- a/Xamarin.Forms.Xaml/PruneIgnoredNodesVisitor.cs +++ b/Xamarin.Forms.Xaml/PruneIgnoredNodesVisitor.cs @@ -5,17 +5,10 @@ namespace Xamarin.Forms.Xaml { class PruneIgnoredNodesVisitor : IXamlNodeVisitor { - public bool StopOnDataTemplate { - get { return false; } - } - - public bool StopOnResourceDictionary { - get { return false; } - } - - public bool VisitChildrenFirst { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => false; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => true; public void Visit(ElementNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs b/Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs index a7da3b98..5ee3bc3e 100644 --- a/Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs +++ b/Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs @@ -12,20 +12,10 @@ namespace Xamarin.Forms.Xaml Dictionary<INode, object> Values { get; } - public bool VisitChildrenFirst - { - get { return false; } - } - - public bool StopOnDataTemplate - { - get { return true; } - } - - public bool StopOnResourceDictionary - { - get { return false; } - } + public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; + public bool StopOnDataTemplate => true; + public bool StopOnResourceDictionary => false; + public bool VisitNodeOnDataTemplate => false; public void Visit(ValueNode node, INode parentNode) { diff --git a/Xamarin.Forms.Xaml/XamlNode.cs b/Xamarin.Forms.Xaml/XamlNode.cs index 752f3845..eccf2075 100644 --- a/Xamarin.Forms.Xaml/XamlNode.cs +++ b/Xamarin.Forms.Xaml/XamlNode.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -7,36 +6,30 @@ using Xamarin.Forms.Internals; namespace Xamarin.Forms.Xaml { - internal interface INode + interface INode { List<string> IgnorablePrefixes { get; set; } - IXmlNamespaceResolver NamespaceResolver { get; } - INode Parent { get; set; } void Accept(IXamlNodeVisitor visitor, INode parentNode); INode Clone(); } - internal interface IValueNode : INode + interface IValueNode : INode { } - internal interface IElementNode : INode, IListNode + interface IElementNode : INode, IListNode { Dictionary<XmlName, INode> Properties { get; } - List<XmlName> SkipProperties { get; } - INameScope Namescope { get; } - XmlType XmlType { get; } - string NamespaceURI { get; } } - internal interface IListNode : INode + interface IListNode : INode { List<INode> CollectionItems { get; } } @@ -56,7 +49,7 @@ namespace Xamarin.Forms.Xaml public IList<XmlType> TypeArguments { get; } } - internal abstract class BaseNode : IXmlLineInfo, INode + abstract class BaseNode : IXmlLineInfo, INode { protected BaseNode(IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) { @@ -66,27 +59,19 @@ namespace Xamarin.Forms.Xaml } public IXmlNamespaceResolver NamespaceResolver { get; } - - public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode); - public INode Parent { get; set; } - public List<string> IgnorablePrefixes { get; set; } - - public bool HasLineInfo() - { - return LineNumber >= 0 && LinePosition >= 0; - } - 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}")] - internal class ValueNode : BaseNode, IValueNode + class ValueNode : BaseNode, IValueNode { public ValueNode(object value, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) @@ -101,19 +86,15 @@ namespace Xamarin.Forms.Xaml visitor.Visit(this, parentNode); } - public override INode Clone() - { - return new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) { - IgnorablePrefixes = IgnorablePrefixes - }; - } + public override INode Clone() => new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) { + IgnorablePrefixes = IgnorablePrefixes + }; } [DebuggerDisplay("{MarkupString}")] - internal class MarkupNode : BaseNode, IValueNode + class MarkupNode : BaseNode, IValueNode { - public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, - int lineposition = -1) + public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) { MarkupString = markupString; @@ -126,15 +107,13 @@ namespace Xamarin.Forms.Xaml visitor.Visit(this, parentNode); } - public override INode Clone() - { - return new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) { - IgnorablePrefixes = IgnorablePrefixes - }; - } + public override INode Clone() => new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) { + IgnorablePrefixes = IgnorablePrefixes + }; } - internal class ElementNode : BaseNode, IValueNode, IElementNode + [DebuggerDisplay("{XmlType.Name}")] + class ElementNode : BaseNode, IValueNode, IElementNode { public ElementNode(XmlType type, string namespaceURI, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) @@ -148,48 +127,48 @@ namespace Xamarin.Forms.Xaml } public Dictionary<XmlName, INode> Properties { get; } - public List<XmlName> SkipProperties { get; } - public List<INode> CollectionItems { get; } - public XmlType XmlType { get; } - public string NamespaceURI { get; } - public INameScope Namescope { get; set; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { - if (!visitor.VisitChildrenFirst) + if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); - if ((!visitor.StopOnDataTemplate || !IsDataTemplate(this, parentNode)) && - (!visitor.StopOnResourceDictionary || !IsResourceDictionary(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 (visitor.VisitChildrenFirst) + + if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); + } - internal static bool IsDataTemplate(INode node, INode parentNode) + bool IsDataTemplate(INode parentNode) { var parentElement = parentNode as IElementNode; INode createContent; - if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) && - createContent == node) + if (parentElement != null && + parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) && + createContent == this) return true; return false; } - internal static bool IsResourceDictionary(INode node, INode parentNode) - { - var enode = node as ElementNode; - return enode.XmlType.Name == "ResourceDictionary"; - } + 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() { @@ -206,7 +185,7 @@ namespace Xamarin.Forms.Xaml } } - internal abstract class RootNode : ElementNode + abstract class RootNode : ElementNode { protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver) : base(xmlType, xmlType.NamespaceUri, nsResolver) { @@ -214,40 +193,39 @@ namespace Xamarin.Forms.Xaml public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { - if (!visitor.VisitChildrenFirst) + if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); - if ((!visitor.StopOnDataTemplate || !IsDataTemplate(this, parentNode)) && - (!visitor.StopOnResourceDictionary || !IsResourceDictionary(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 (visitor.VisitChildrenFirst) + + if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); } } - internal class ListNode : BaseNode, IListNode, IValueNode + class ListNode : BaseNode, IListNode, IValueNode { - public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, - int lineposition = -1) : base(namespaceResolver, linenumber, lineposition) + public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1) + : base(namespaceResolver, linenumber, lineposition) { CollectionItems = nodes.ToList(); } public XmlName XmlName { get; set; } - public List<INode> CollectionItems { get; set; } public override void Accept(IXamlNodeVisitor visitor, INode parentNode) { - if (!visitor.VisitChildrenFirst) + if (visitor.VisitingMode == TreeVisitingMode.TopDown) visitor.Visit(this, parentNode); foreach (var node in CollectionItems) node.Accept(visitor, this); - if (visitor.VisitChildrenFirst) + if (visitor.VisitingMode == TreeVisitingMode.BottomUp) visitor.Visit(this, parentNode); } @@ -262,12 +240,11 @@ namespace Xamarin.Forms.Xaml } } - internal static class INodeExtensions + static class INodeExtensions { public static bool SkipPrefix(this INode node, string prefix) { - do - { + do { if (node.IgnorablePrefixes != null && node.IgnorablePrefixes.Contains(prefix)) return true; node = node.Parent; diff --git a/Xamarin.Forms.Xaml/XamlNodeVisitor.cs b/Xamarin.Forms.Xaml/XamlNodeVisitor.cs index e0b1db32..874a5fbd 100644 --- a/Xamarin.Forms.Xaml/XamlNodeVisitor.cs +++ b/Xamarin.Forms.Xaml/XamlNodeVisitor.cs @@ -2,12 +2,11 @@ namespace Xamarin.Forms.Xaml { - internal interface IXamlNodeVisitor + interface IXamlNodeVisitor { - bool VisitChildrenFirst { get; } - + TreeVisitingMode VisitingMode { get; } bool StopOnDataTemplate { get; } - + bool VisitNodeOnDataTemplate { get; } bool StopOnResourceDictionary { get; } void Visit(ValueNode node, INode parentNode); @@ -17,22 +16,27 @@ namespace Xamarin.Forms.Xaml void Visit(ListNode node, INode parentNode); } - internal class XamlNodeVisitor : IXamlNodeVisitor + enum TreeVisitingMode { + TopDown, + BottomUp + } + + class XamlNodeVisitor : IXamlNodeVisitor { readonly Action<INode, INode> action; - public XamlNodeVisitor(Action<INode, INode> action, bool visitChildrenFirst = false, bool stopOnDataTemplate = false) + public XamlNodeVisitor(Action<INode, INode> action, TreeVisitingMode visitingMode = TreeVisitingMode.TopDown, bool stopOnDataTemplate = false, bool visitNodeOnDataTemplate = true) { this.action = action; - VisitChildrenFirst = visitChildrenFirst; + VisitingMode = visitingMode; StopOnDataTemplate = stopOnDataTemplate; + VisitNodeOnDataTemplate = visitNodeOnDataTemplate; } - public bool VisitChildrenFirst { get; } - + public TreeVisitingMode VisitingMode { get; } public bool StopOnDataTemplate { get; } - public bool StopOnResourceDictionary { get; private set; } + public bool VisitNodeOnDataTemplate { get; } public void Visit(ValueNode node, INode parentNode) { |