TNode
TNode represents a single node in the XML representation
of a Test or TestResult. It replaces System.Xml.XmlNode and
System.Xml.Linq.XElement, providing a minimal set of methods
for operating on the XML in a platform-independent manner.
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
namespace NUnit.Framework.Interfaces
{
public class TNode
{
private class NodeFilter
{
private string _nodeName;
private string _propName;
private string _propValue;
public NodeFilter(string xpath)
{
_nodeName = xpath;
int num = xpath.IndexOf('[');
if (num >= 0) {
if (!xpath.EndsWith("]"))
throw new ArgumentException("Invalid property expression", "xpath");
_nodeName = xpath.Substring(0, num);
string text = xpath.Substring(num + 1, xpath.Length - num - 2);
int num2 = text.IndexOf('=');
if (num2 < 0 || text[0] != '@')
throw new ArgumentException("Invalid property expression", "xpath");
_propName = text.Substring(1, num2 - 1).Trim();
_propValue = text.Substring(num2 + 1).Trim(' ', '"', '\'');
}
}
public bool Pass(TNode node)
{
if (node.Name != _nodeName)
return false;
if (_propName == null)
return true;
return node.Attributes[_propName] == _propValue;
}
}
private static readonly Regex InvalidXmlCharactersRegex = new Regex("[^\t\n\r -�]|([�-�](?![�-�]))|((?<![�-�])[�-�])");
public string Name { get; set; }
public string Value { get; set; }
public bool ValueIsCDATA { get; set; }
public AttributeDictionary Attributes { get; set; }
public NodeList ChildNodes { get; set; }
public TNode FirstChild {
get {
if (ChildNodes.Count != 0)
return ChildNodes[0];
return null;
}
}
public string OuterXml {
get {
StringWriter stringWriter = new StringWriter();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.ConformanceLevel = ConformanceLevel.Fragment;
using (XmlWriter writer = XmlWriter.Create(stringWriter, xmlWriterSettings))
WriteTo(writer);
return stringWriter.ToString();
}
}
public TNode(string name)
{
Name = name;
Attributes = new AttributeDictionary();
ChildNodes = new NodeList();
}
public TNode(string name, string value)
: this(name, value, false)
{
}
public TNode(string name, string value, bool valueIsCDATA)
: this(name)
{
Value = value;
ValueIsCDATA = valueIsCDATA;
}
public static TNode FromXml(string xmlText)
{
return FromXml(XElement.Parse(xmlText));
}
public TNode AddElement(string name)
{
TNode tNode = new TNode(name);
ChildNodes.Add(tNode);
return tNode;
}
public TNode AddElement(string name, string value)
{
TNode tNode = new TNode(name, EscapeInvalidXmlCharacters(value));
ChildNodes.Add(tNode);
return tNode;
}
public TNode AddElementWithCDATA(string name, string value)
{
TNode tNode = new TNode(name, EscapeInvalidXmlCharacters(value), true);
ChildNodes.Add(tNode);
return tNode;
}
public void AddAttribute(string name, string value)
{
Attributes.Add(name, EscapeInvalidXmlCharacters(value));
}
public TNode SelectSingleNode(string xpath)
{
NodeList nodeList = SelectNodes(xpath);
if (nodeList.Count <= 0)
return null;
return nodeList[0];
}
public NodeList SelectNodes(string xpath)
{
return ApplySelection(new NodeList {
this
}, xpath);
}
public void WriteTo(XmlWriter writer)
{
writer.WriteStartElement(Name);
foreach (string key in Attributes.Keys) {
writer.WriteAttributeString(key, Attributes[key]);
}
if (Value != null) {
if (ValueIsCDATA)
WriteCDataTo(writer);
else
writer.WriteString(Value);
}
foreach (TNode childNode in ChildNodes) {
childNode.WriteTo(writer);
}
writer.WriteEndElement();
}
private static TNode FromXml(XElement xElement)
{
TNode tNode = new TNode(xElement.Name.ToString(), xElement.Value);
foreach (XAttribute item in xElement.Attributes()) {
tNode.AddAttribute(item.Name.ToString(), item.Value);
}
foreach (XElement item2 in xElement.Elements()) {
tNode.ChildNodes.Add(FromXml(item2));
}
return tNode;
}
private static NodeList ApplySelection(NodeList nodeList, string xpath)
{
Guard.ArgumentNotNullOrEmpty(xpath, "xpath");
if (xpath[0] == '/')
throw new ArgumentException("XPath expressions starting with '/' are not supported", "xpath");
if (xpath.IndexOf("//") >= 0)
throw new ArgumentException("XPath expressions with '//' are not supported", "xpath");
string xpath2 = xpath;
string text = null;
int num = xpath.IndexOf('/');
if (num >= 0) {
xpath2 = xpath.Substring(0, num);
text = xpath.Substring(num + 1);
}
NodeList nodeList2 = new NodeList();
NodeFilter nodeFilter = new NodeFilter(xpath2);
foreach (TNode node in nodeList) {
foreach (TNode childNode in node.ChildNodes) {
if (nodeFilter.Pass(childNode))
nodeList2.Add(childNode);
}
}
if (text == null)
return nodeList2;
return ApplySelection(nodeList2, text);
}
private static string EscapeInvalidXmlCharacters(string str)
{
if (str == null)
return null;
return InvalidXmlCharactersRegex.Replace(str, (Match match) => CharToUnicodeSequence(match.Value[0]));
}
private static string CharToUnicodeSequence(char symbol)
{
int num = symbol;
return string.Format("\\u{0}", num.ToString("x4"));
}
private void WriteCDataTo(XmlWriter writer)
{
int num = 0;
string value = Value;
while (true) {
int num2 = value.IndexOf("]]>", num);
if (num2 < 0)
break;
writer.WriteCData(value.Substring(num, num2 - num + 2));
num = num2 + 2;
if (num >= value.Length)
return;
}
if (num > 0)
writer.WriteCData(value.Substring(num));
else
writer.WriteCData(value);
}
}
}