Files
Umbraco-CMS/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs

1147 lines
42 KiB
C#
Raw Normal View History

2013-11-07 17:16:22 +01:00
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Xml;
using Umbraco.Core.Xml.XPath;
using NUnit.Framework;
namespace Umbraco.Tests.CoreXml
{
[TestFixture]
public class NavigableNavigatorTests
{
[Test]
public void NewNavigatorIsAtRoot()
{
const string xml = @"<root><item1 /><item2 /></root>";
var doc = XmlHelper.CreateXPathDocument(xml);
var nav = doc.CreateNavigator();
Assert.AreEqual(XPathNodeType.Root, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
var source = new TestSource5();
nav = new NavigableNavigator(source);
Assert.AreEqual(XPathNodeType.Root, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
}
[Test]
public void NativeXmlValues()
{
const string xml = @"<root>
<wrap>
<item1 />
<item2></item2>
<item2a> </item2a>
<item2b>
</item2b>
<item2c><![CDATA[
]]></item2c>
<item3>blah</item3>
<item3a>
blah
</item3a>
<item4>
<subitem x=""1"">bam</subitem>
</item4>
<item5><![CDATA[
]]></item5>
</wrap>
</root>";
var doc = XmlHelper.CreateXPathDocument(xml);
var nav = doc.CreateNavigator();
NavigatorValues(nav, true);
}
[Test]
public void NavigableXmlValues()
{
var source = new TestSource6();
var nav = new NavigableNavigator(source);
NavigatorValues(nav, false);
}
static void NavigatorValues(XPathNavigator nav, bool native)
{
// in non-native we can't have Value dump everything, else
// we'd dump the entire database? Makes not much sense.
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("root", nav.Name);
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("wrap", nav.Name);
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("item1", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2a", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2b", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
// we have no way to tell the navigable that a value is CDATA
// so the rule is, if it's null it's not there, anything else is there
// and the filtering has to be done when building the content
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2c", nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf()); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item3", nav.Name);
Assert.AreEqual("blah", nav.Value); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("blah", nav.Value);
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item3a", nav.Name);
Assert.AreEqual("\n blah\n ", nav.Value.Lf()); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("\n blah\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item4", nav.Name);
Assert.AreEqual("bam", nav.Value); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("subitem", nav.Name);
Assert.AreEqual("bam", nav.Value); // ok since we're in a fragment
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("bam", nav.Value);
Assert.IsFalse(nav.MoveToNext());
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual("subitem", nav.Name);
Assert.IsFalse(nav.MoveToNext());
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual("item4", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item5", nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual("\n ", nav.Value.Lf());
}
[Test]
public void Navigate()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
Assert.AreEqual("", nav.Name); // because we're at root
nav.MoveToFirstChild();
Assert.AreEqual("root", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual("type1", nav.Name); // our first content
nav.MoveToFirstAttribute();
Assert.AreEqual("id", nav.Name);
Assert.AreEqual("1", nav.Value);
nav.MoveToNextAttribute();
Assert.AreEqual("prop1", nav.Name);
Assert.AreEqual("1:p1", nav.Value);
nav.MoveToNextAttribute();
Assert.AreEqual("prop2", nav.Name);
Assert.AreEqual("1:p2", nav.Value);
Assert.IsFalse(nav.MoveToNextAttribute());
nav.MoveToParent();
nav.MoveToFirstChild();
Assert.AreEqual("prop3", nav.Name);
Assert.AreEqual("1:p3", nav.Value);
Assert.IsFalse(nav.MoveToNext());
}
[Test]
public void NavigateMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
nav.MoveToFirstChild();
Assert.AreEqual("root", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual("type1", nav.Name); // our content
nav.MoveToFirstChild();
Assert.AreEqual("prop1", nav.Name); // our property
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
nav.MoveToFirstChild();
// "<data><item1>poo</item1><item2 xx=\"33\" /><item2 xx=\"34\" /></data>"
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("data", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("item1", nav.Name);
nav.MoveToNext();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("item2", nav.Name);
nav.MoveToParent();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("data", nav.Name);
nav.MoveToParent();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("prop1", nav.Name);
}
[Test]
public void OuterXmlBasic()
{
const string xml = @"<root id=""-1"" />";
var doc = XmlHelper.CreateXPathDocument(xml);
var nnav = doc.CreateNavigator();
Assert.AreEqual(xml, nnav.OuterXml);
var source = new TestSource0();
var nav = new NavigableNavigator(source);
Assert.AreEqual(xml, nav.OuterXml);
}
[Test]
public void OuterXml()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
const string xml = @"<root id=""-1"" prop1="""" prop2="""">
<type1 id=""1"" prop1=""1:p1"" prop2=""1:p2"">
<prop3>1:p3</prop3>
</type1>
</root>";
Assert.AreEqual(xml.Lf(), nav.OuterXml.Lf());
}
[Test]
public void OuterXmlMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
const string outerXml = @"<root id=""-1"">
<type1 id=""1"">
<prop1>
<data>
<item1>poo</item1>
<item2 xx=""33"" />
<item2 xx=""34"" />
</data>
</prop1>
</type1>
</root>";
Assert.AreEqual(outerXml.Lf(), nav.OuterXml.Lf());
}
[Test]
public void Query()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//type1");
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
iterator = nav.Select("//* [@prop1='1:p1']");
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
}
[Test]
public void QueryMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
var doc = XmlHelper.CreateXPathDocument("<data><item1>poo</item1><item2 xx=\"33\" /><item2 xx=\"34\" /></data>");
var docNav = doc.CreateNavigator();
var docIter = docNav.Select("//item2 [@xx=33]");
Assert.AreEqual(1, docIter.Count);
Assert.AreEqual("", docIter.Current.Name);
docIter.MoveNext();
Assert.AreEqual("item2", docIter.Current.Name);
var iterator = nav.Select("//item2 [@xx=33]");
Assert.AreEqual(1, iterator.Count);
Assert.AreEqual("", iterator.Current.Name);
iterator.MoveNext();
Assert.AreEqual("item2", iterator.Current.Name);
}
[Test]
public void QueryWithVariables()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//* [@prop1=$var]", new XPathVariable("var", "1:p1"));
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
}
[Test]
public void QueryMixedWithVariables()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//item2 [@xx=$var]", new XPathVariable("var", "33"));
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("item2", iterator.Current.Name);
}
[Test]
public void MixedWithNoValue()
{
var source = new TestSource4();
var nav = new NavigableNavigator(source);
var doc = XmlHelper.CreateXPathDocument(@"<root id=""-1"">
<type1 id=""1""><prop1><data value=""value""/></prop1><prop2>dang</prop2></type1>
<type1 id=""2""><prop1 /><prop2></prop2></type1>
<type1 id=""3""><prop1 /><prop2 /></type1>
</root>");
var docNav = doc.CreateNavigator();
docNav.MoveToRoot();
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.AreEqual("root", docNav.Name);
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.AreEqual("type1", docNav.Name);
Assert.IsTrue(docNav.MoveToNext());
Assert.AreEqual("type1", docNav.Name);
Assert.IsTrue(docNav.MoveToNext());
Assert.AreEqual("type1", docNav.Name);
Assert.IsFalse(docNav.MoveToNext());
docNav.MoveToRoot();
var docOuter = docNav.OuterXml;
nav.MoveToRoot();
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("root", nav.Name);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("type1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("type1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("type1", nav.Name);
Assert.IsFalse(nav.MoveToNext());
nav.MoveToRoot();
var outer = nav.OuterXml;
Assert.AreEqual(docOuter, outer);
}
[Test]
[Ignore("NavigableNavigator does not implement IHasXmlNode.")]
public void XmlNodeList()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("/*");
// but, that requires that the underlying navigator implements IHasXmlNode
// so it is possible to obtain nodes from the navigator - not possible yet
var nodes = XmlNodeListFactory.CreateNodeList(iterator);
Assert.AreEqual(nodes.Count, 1);
var node = nodes[0];
Assert.AreEqual(3, node.Attributes.Count);
Assert.AreEqual("1", node.Attributes["id"].Value);
Assert.AreEqual("1:p1", node.Attributes["prop1"].Value);
Assert.AreEqual("1:p2", node.Attributes["prop2"].Value);
Assert.AreEqual(1, node.ChildNodes.Count);
Assert.AreEqual("prop3", node.FirstChild.Name);
Assert.AreEqual("1:p3", node.FirstChild.Value);
}
[Test]
public void CloneIsSafe()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
TestContent content;
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual("root", nav.Name); // at -1
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop2", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// at that point nav is at /root/1/3
var clone = nav.Clone() as NavigableNavigator;
// move nav to /root/1/5 and ensure that clone stays at /root/1/3
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(5, (nav.UnderlyingObject as TestContent).Id);
Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position);
Assert.AreEqual(3, (clone.UnderlyingObject as TestContent).Id);
// move nav to /root/2
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(2, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop1", nav.Name);
Assert.AreEqual("p21", nav.Value);
// move clone to .. /root/1
Assert.IsTrue(clone.MoveToParent());
// clone has not been corrupted by nav
Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position);
Assert.AreEqual(1, (clone.UnderlyingObject as TestContent).Id);
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void SelectById(int id)
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
var iter = nav.Select(string.Format("//* [@id={0}]", id));
Assert.IsTrue(iter.MoveNext());
var current = iter.Current as NavigableNavigator;
Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position);
Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id);
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void SelectByIdWithVariable(int id)
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
var iter = nav.Select("//* [@id=$id]", new XPathVariable("id", id.ToString(CultureInfo.InvariantCulture)));
Assert.IsTrue(iter.MoveNext());
var current = iter.Current as NavigableNavigator;
Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position);
Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id);
}
[Test]
public void MoveToId()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
// move to /root/1/3
Assert.IsTrue(nav.MoveToId("3"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// move to /root/1
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// move to /root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// move to /root/1
Assert.IsTrue(nav.MoveToId("1"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// move to /root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// move to /root
Assert.IsTrue(nav.MoveToId("-1"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// get lost
Assert.IsFalse(nav.MoveToId("666"));
}
[Test]
public void RootedNavigator()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source, source.Get(1));
// go to (/root) /1
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) /1/prop1
Assert.IsTrue(nav.MoveToFirstChild());
// go to (/root) /1/prop2
Assert.IsTrue(nav.MoveToNext());
// go to (/root) /1/3
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) /1
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) ie root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// can't go there
Assert.IsFalse(nav.MoveToId("2"));
}
[TestCase(true, true)]
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public void XsltDebugModeAndSortOrder(bool native, bool debug)
{
const string xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<root>
<node id=""1"" isDoc=""1"">
<title>title-1</title>
<node id=""3"" isDoc=""1"">
<title>title-3</title>
<node id=""7"" isDoc=""1"">
<title>title-7</title>
</node>
<node id=""8"" isDoc=""1"">
<title>title-8</title>
</node>
</node>
<node id=""5"" isDoc=""1"">
<title>title-5</title>
</node>
</node>
<node id=""2"" isDoc=""1"">
<title>title-2</title>
<node id=""4"" isDoc=""1"">
<title>title-4</title>
</node>
<node id=""6"" isDoc=""1"">
<title>title-6</title>
</node>
</node>
</root>
";
const string xslt = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp ""&#x00A0;"">
]>
<xsl:stylesheet
version=""1.0""
xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""
xmlns:msxml=""urn:schemas-microsoft-com:xslt""
xmlns:umbraco.library=""urn:umbraco.library"" xmlns:Exslt.ExsltCommon=""urn:Exslt.ExsltCommon"" xmlns:Exslt.ExsltDatesAndTimes=""urn:Exslt.ExsltDatesAndTimes"" xmlns:Exslt.ExsltMath=""urn:Exslt.ExsltMath"" xmlns:Exslt.ExsltRegularExpressions=""urn:Exslt.ExsltRegularExpressions"" xmlns:Exslt.ExsltStrings=""urn:Exslt.ExsltStrings"" xmlns:Exslt.ExsltSets=""urn:Exslt.ExsltSets"" xmlns:Examine=""urn:Examine""
exclude-result-prefixes=""msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets Examine "">
<xsl:output method=""xml"" omit-xml-declaration=""yes"" />
<xsl:param name=""currentPage""/>
<xsl:template match=""/"">
<!-- <xsl:for-each select=""/root/* [@isDoc]""> -->
<!-- <xsl:for-each select=""$currentPage/root/* [@isDoc]""> -->
<xsl:for-each select=""/macro/nav/root/* [@isDoc]"">
<xsl:text>! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
</xsl:text>
<xsl:for-each select=""./* [@isDoc]"">
<xsl:text>!! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
</xsl:text>
<xsl:for-each select=""./* [@isDoc]"">
<xsl:text>!!! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
";
const string expected = @"! title-1
!! title-3
!!! title-7
!!! title-8
!! title-5
! title-2
!! title-4
!! title-6
";
// see http://www.onenaught.com/posts/352/xslt-performance-tip-dont-indent-output
// why aren't we using an XmlWriter here?
var transform = new XslCompiledTransform(debug);
var xmlReader = new XmlTextReader(new StringReader(xslt))
{
EntityHandling = EntityHandling.ExpandEntities
};
var xslResolver = new XmlUrlResolver
{
Credentials = CredentialCache.DefaultCredentials
};
var args = new XsltArgumentList();
// .Default is more restrictive than .TrustedXslt
transform.Load(xmlReader, XsltSettings.Default, xslResolver);
XPathNavigator macro;
if (!native)
{
var source = new TestSource7();
var nav = new NavigableNavigator(source);
//args.AddParam("currentPage", string.Empty, nav.Clone());
var x = new XmlDocument();
x.LoadXml(xml);
macro = new MacroNavigator(new[]
{
// it even fails like that => macro nav. issue?
new MacroNavigator.MacroParameter("nav", x.CreateNavigator()) // nav.Clone())
}
);
}
else
{
var doc = new XmlDocument();
doc.LoadXml("<macro />");
var nav = doc.CreateElement("nav");
doc.DocumentElement.AppendChild(nav);
var x = new XmlDocument();
x.LoadXml(xml);
nav.AppendChild(doc.ImportNode(x.DocumentElement, true));
macro = doc.CreateNavigator();
}
var writer = new StringWriter();
transform.Transform(macro, args, writer);
// this was working with native, debug and non-debug
// this was working with macro nav, non-debug
// but was NOT working (changing the order of nodes) with macro nav, debug
// was due to an issue with macro nav IsSamePosition, fixed
//Console.WriteLine("--------");
//Console.WriteLine(writer.ToString());
Assert.AreEqual(expected.Lf(), writer.ToString().Lf());
}
[Test]
public void WhiteSpacesAndEmptyValues()
{
// "When Microsofts DOM builder receives a text node from the parser
// that contains only white space, it is thrown away." - so if it's ONLY
// spaces, it's nothing, but spaces are NOT trimmed.
// For attributes, spaces are preserved even when there's only spaces.
var doc = XmlHelper.CreateXPathDocument(@"<root>
<item><prop/></item>
<item><prop></prop></item>
<item><prop> </prop></item>
<item><prop>
</prop></item>
<item><prop> ooo </prop></item>
<item><prop> ooo
</prop></item>
<item x=""""/>
<item x="" ""/>
</root>");
var docNav = doc.CreateNavigator();
Assert.AreEqual(@"<root>
<item>
<prop />
</item>
<item>
<prop></prop>
</item>
<item>
<prop></prop>
</item>
<item>
<prop></prop>
</item>
<item>
<prop> ooo </prop>
</item>
<item>
<prop> ooo
</prop>
</item>
<item x="""" />
<item x="" "" />
</root>".Lf(), docNav.OuterXml.Lf());
docNav.MoveToRoot();
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsTrue(docNav.IsEmptyElement);
Assert.IsTrue(docNav.MoveToParent());
Assert.IsTrue(docNav.MoveToNext());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsFalse(docNav.IsEmptyElement);
Assert.AreEqual("", docNav.Value); // contains an empty text node
Assert.IsTrue(docNav.MoveToParent());
Assert.IsTrue(docNav.MoveToNext());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsFalse(docNav.IsEmptyElement);
Assert.AreEqual("", docNav.Value); // contains an empty text node
var source = new TestSource8();
var nav = new NavigableNavigator(source);
// shows how whitespaces are handled by NavigableNavigator
Assert.AreEqual(@"<root id=""-1"" attr="""">
<item id=""1"" attr="""">
<prop />
</item>
<item id=""2"" attr="""">
<prop></prop>
</item>
<item id=""3"" attr="" "">
<prop> </prop>
</item>
<item id=""4"" attr="""">
<prop>
</prop>
</item>
<item id=""5"" attr="" ooo "">
<prop> ooo </prop>
</item>
</root>".Lf(), nav.OuterXml.Lf());
}
}
#region Navigable implementation
class TestPropertyType : INavigableFieldType
{
public TestPropertyType(string name, bool isXmlContent = false, Func<object, string> xmlStringConverter = null)
{
Name = name;
IsXmlContent = isXmlContent;
XmlStringConverter = xmlStringConverter;
}
public string Name { get; private set; }
public bool IsXmlContent { get; private set; }
public Func<object, string> XmlStringConverter { get; private set; }
}
class TestContentType : INavigableContentType
{
public TestContentType(TestSourceBase source, string name, params INavigableFieldType[] properties)
{
Source = source;
Name = name;
FieldTypes = properties;
}
public TestSourceBase Source { get; private set; }
public string Name { get; private set; }
public INavigableFieldType[] FieldTypes { get; protected set; }
}
class TestRootContentType : TestContentType
{
public TestRootContentType(TestSourceBase source, params INavigableFieldType[] properties)
: base(source, "root")
{
FieldTypes = properties;
}
public TestContentType CreateType(string name, params INavigableFieldType[] properties)
{
return new TestContentType(Source, name, FieldTypes.Union(properties).ToArray());
}
}
class TestContent : INavigableContent
{
public TestContent(TestContentType type, int id, int parentId)
{
_type = type;
Id = id;
ParentId = parentId;
}
private readonly TestContentType _type;
public int Id { get; private set; }
public int ParentId { get; private set; }
public INavigableContentType Type { get { return _type; } }
public IList<int> ChildIds { get; private set; }
public object Value(int id)
{
var fieldType = _type.FieldTypes[id] as TestPropertyType;
if (fieldType == null) throw new Exception("Oops");
var value = FieldValues[id];
var isAttr = id <= _type.Source.LastAttributeIndex;
// null => return null
if (value == null) return null;
// attribute => return string value
if (isAttr) return value.ToString();
// has a converter => use the converter
if (fieldType.XmlStringConverter != null)
return fieldType.XmlStringConverter(value);
// not a string => return value as a string
var s = value as string;
if (s == null) return value.ToString();
// xml content... try xml
if (fieldType.IsXmlContent)
{
XPathDocument doc;
if (XmlHelper.TryCreateXPathDocumentFromPropertyValue(s, out doc))
return doc.CreateNavigator();
}
// return the string
// even if it's xml that can't be parsed...
return s;
}
// locals
public object[] FieldValues { get; private set; }
public TestContent WithChildren(params int[] childIds)
{
ChildIds = childIds;
return this;
}
public TestContent WithValues(params object[] values)
{
FieldValues = values == null ? new object[] {null} : values;
return this;
}
}
class TestRootContent : TestContent
{
public TestRootContent(TestContentType type)
: base(type, -1, -1)
{ }
}
abstract class TestSourceBase : INavigableSource
{
protected readonly Dictionary<int, TestContent> Content = new Dictionary<int, TestContent>();
public INavigableContent Get(int id)
{
return Content.ContainsKey(id) ? Content[id] : null;
}
public int LastAttributeIndex { get; protected set; }
public INavigableContent Root { get; protected set; }
}
#endregion
#region Navigable sources
class TestSource0 : TestSourceBase
{
public TestSource0()
{
LastAttributeIndex = -1;
var type = new TestRootContentType(this);
Root = new TestRootContent(type);
}
}
class TestSource1 : TestSourceBase
{
public TestSource1()
{
// last attribute index is 1 - meaning properties 0 and 1 are attributes, 2+ are elements
// then, fieldValues must have adequate number of items
LastAttributeIndex = 1;
var prop1 = new TestPropertyType("prop1");
var prop2 = new TestPropertyType("prop2");
var prop3 = new TestPropertyType("prop3");
var type = new TestRootContentType(this, prop1, prop2);
var type1 = type.CreateType("type1", prop3);
Content[1] = new TestContent(type1, 1, -1).WithValues("1:p1", "1:p2", "1:p3");
Root = new TestRootContent(type).WithValues("", "").WithChildren(1);
}
}
class TestSource2 : TestSourceBase
{
public TestSource2()
{
LastAttributeIndex = -1;
var prop1 = new TestPropertyType("prop1", true);
var type = new TestRootContentType(this);
var type1 = type.CreateType("type1", prop1);
const string xml = "<data><item1>poo</item1><item2 xx=\"33\" /><item2 xx=\"34\" /></data>";
Content[1] = new TestContent(type1, 1, 1).WithValues(xml);
Root = new TestRootContent(type).WithChildren(1);
}
}
2013-11-07 17:16:22 +01:00
class TestSource4 : TestSourceBase
{
public TestSource4()
{
LastAttributeIndex = -1;
var prop1 = new TestPropertyType("prop1", true);
var prop2 = new TestPropertyType("prop2");
var type = new TestRootContentType(this);
var type1 = type.CreateType("type1", prop1, prop2);
Content[1] = new TestContent(type1, 1, -1).WithValues("<data value=\"value\"/>", "dang");
Content[2] = new TestContent(type1, 2, -1).WithValues(null, "");
Content[3] = new TestContent(type1, 3, -1).WithValues(null, null);
Root = new TestRootContent(type).WithChildren(1, 2, 3);
}
}
class TestSource5 : TestSourceBase
{
public TestSource5()
{
LastAttributeIndex = -1;
var prop1 = new TestPropertyType("prop1");
var prop2 = new TestPropertyType("prop2");
var type = new TestRootContentType(this);
var type1 = type.CreateType("type1", prop1, prop2);
Content[1] = new TestContent(type1, 1, -1).WithValues("p11", "p12").WithChildren(3, 5);
Content[2] = new TestContent(type1, 2, -1).WithValues("p21", "p22").WithChildren(4, 6);
Content[3] = new TestContent(type1, 3, 1).WithValues("p31", "p32");
Content[4] = new TestContent(type1, 4, 2).WithValues("p41", "p42");
Content[5] = new TestContent(type1, 5, 1).WithValues("p51", "p52");
Content[6] = new TestContent(type1, 6, 2).WithValues("p61", "p62");
Root = new TestRootContent(type).WithChildren(1, 2);
}
}
class TestSource6 : TestSourceBase
{
//<root>
// <wrap>
// <item1 />
// <item2></item2>
// <item2a> </item2a>
// <item2b>
// </item2b>
// <item2c><![CDATA[
// ]]></item2c>
// <item3>blah</item3>
// <item4>
// <subitem x=""1"">bam</subitem>
// </item4>
// <item5>
// </item5>
// </wrap>
//</root>
public TestSource6()
{
LastAttributeIndex = -1;
var type = new TestRootContentType(this);
var type1 = type.CreateType("wrap",
new TestPropertyType("item1"),
new TestPropertyType("item2"),
new TestPropertyType("item2a"),
new TestPropertyType("item2b"),
new TestPropertyType("item2c"),
new TestPropertyType("item3"),
new TestPropertyType("item3a"),
new TestPropertyType("item4", true),
new TestPropertyType("item5", true)
);
Content[1] = new TestContent(type1, 1, -1)
.WithValues(
null,
null,
null,
null,
"\n ",
"blah",
"\n blah\n ",
"<subitem x=\"1\">bam</subitem>",
"\n "
);
Root = new TestRootContent(type).WithChildren(1);
}
}
class TestSource7 : TestSourceBase
{
public TestSource7()
{
LastAttributeIndex = 0;
var prop1 = new TestPropertyType("isDoc");
var prop2 = new TestPropertyType("title");
var type = new TestRootContentType(this, prop1);
var type1 = type.CreateType("node", prop1, prop2);
Content[1] = new TestContent(type1, 1, -1).WithValues(1, "title-1").WithChildren(3, 5);
Content[2] = new TestContent(type1, 2, -1).WithValues(1, "title-2").WithChildren(4, 6);
Content[3] = new TestContent(type1, 3, 1).WithValues(1, "title-3").WithChildren(7, 8);
Content[4] = new TestContent(type1, 4, 2).WithValues(1, "title-4");
Content[5] = new TestContent(type1, 5, 1).WithValues(1, "title-5");
Content[6] = new TestContent(type1, 6, 2).WithValues(1, "title-6");
Content[7] = new TestContent(type1, 7, 3).WithValues(1, "title-7");
Content[8] = new TestContent(type1, 8, 3).WithValues(1, "title-8");
Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2);
}
}
class TestSource8 : TestSourceBase
{
public TestSource8()
{
LastAttributeIndex = 0;
var attr = new TestPropertyType("attr");
var prop = new TestPropertyType("prop");
var type = new TestRootContentType(this, attr);
var type1 = type.CreateType("item", attr, prop);
Content[1] = new TestContent(type1, 1, -1).WithValues(null, null);
Content[2] = new TestContent(type1, 2, -1).WithValues("", "");
Content[3] = new TestContent(type1, 3, -1).WithValues(" ", " ");
Content[4] = new TestContent(type1, 4, -1).WithValues("", "\n");
Content[5] = new TestContent(type1, 5, -1).WithValues(" ooo ", " ooo ");
Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2, 3, 4, 5);
}
}
#endregion
}