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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml;
namespace NUnit.Framework.Interfaces
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
[DebuggerDisplay("{OuterXml}")]
public sealed class TNode
{
[System.Runtime.CompilerServices.Nullable(0)]
private sealed class NodeFilter
{
private readonly string _nodeName;
[System.Runtime.CompilerServices.Nullable(2)]
private readonly string _propName;
[System.Runtime.CompilerServices.Nullable(2)]
private readonly string _propValue;
private static readonly char[] TrimChars = new char[3] {
' ',
'"',
'\''
};
public NodeFilter(string xpath)
{
_nodeName = xpath;
int num = xpath.IndexOf('[');
if (num >= 0) {
if (!xpath.EndsWith("]", StringComparison.Ordinal))
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(TrimChars);
}
}
public bool Pass(TNode node)
{
if (node.Name != _nodeName)
return false;
if (_propName == null)
return true;
return node.Attributes[_propName] == _propValue;
}
}
[System.Runtime.CompilerServices.Nullable(0)]
public readonly struct NodeList : IEnumerable<TNode>, IEnumerable
{
private static readonly List<TNode> EmptyList = new List<TNode>();
private readonly TNode _parent;
public TNode this[int index] {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
if (_parent._childNodes == null || (uint)index >= (uint)_parent._childNodes.Count)
ThrowArgumentOutOfRangeException(index);
return _parent._childNodes[index];
}
}
public int Count {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
List<TNode> childNodes = _parent._childNodes;
if (childNodes == null)
return 0;
return childNodes.Count;
}
}
internal NodeList(TNode parent)
{
_parent = parent;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowArgumentOutOfRangeException(int index)
{
throw new ArgumentOutOfRangeException("index", index, "Index was out or range of valid values");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public List<TNode>.Enumerator GetEnumerator()
{
return _parent._childNodes?.GetEnumerator() ?? EmptyList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator<TNode> IEnumerable<TNode>.GetEnumerator()
{
return GetEnumerator();
}
}
[System.Runtime.CompilerServices.Nullable(0)]
public readonly struct AttributeDictionary
{
private static readonly Dictionary<string, string> EmptyDictionary = new Dictionary<string, string>();
private readonly TNode _parent;
[System.Runtime.CompilerServices.Nullable(2)]
public string this[string key] {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: System.Runtime.CompilerServices.Nullable(2)]
get {
string value = null;
_parent._attributes?.TryGetValue(key, out value);
return value;
}
}
public int Count {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
Dictionary<string, string> attributes = _parent._attributes;
if (attributes == null)
return 0;
return attributes.Count;
}
}
internal AttributeDictionary(TNode parent)
{
_parent = parent;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1,
1
})]
public Dictionary<string, string>.Enumerator GetEnumerator()
{
return _parent._attributes?.GetEnumerator() ?? EmptyDictionary.GetEnumerator();
}
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
private List<TNode> _childNodes;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1,
1
})]
private Dictionary<string, string> _attributes;
public string Name { get; }
[System.Runtime.CompilerServices.Nullable(2)]
[field: System.Runtime.CompilerServices.Nullable(2)]
public string Value {
[System.Runtime.CompilerServices.NullableContext(2)]
get;
[System.Runtime.CompilerServices.NullableContext(2)]
set;
}
public bool ValueIsCDATA { get; set; }
public AttributeDictionary Attributes => new AttributeDictionary(this);
public NodeList ChildNodes => new NodeList(this);
[System.Runtime.CompilerServices.Nullable(2)]
public TNode FirstChild {
[System.Runtime.CompilerServices.NullableContext(2)]
get {
List<TNode> childNodes = _childNodes;
if (childNodes == null || childNodes.Count != 0)
return ChildNodes[0];
return null;
}
}
public string OuterXml {
get {
using (StringWriter stringWriter = new StringWriter()) {
using (XmlWriter writer = XmlWriter.Create(stringWriter, XmlExtensions.FragmentWriterSettings))
WriteTo(writer);
return stringWriter.ToString();
}
}
}
public TNode(string name)
{
Name = name;
}
public TNode(string name, [System.Runtime.CompilerServices.Nullable(2)] string value)
: this(name, value, false)
{
}
public TNode(string name, [System.Runtime.CompilerServices.Nullable(2)] string value, bool valueIsCDATA)
: this(name)
{
Value = XmlExtensions.EscapeInvalidXmlCharacters(value);
ValueIsCDATA = valueIsCDATA;
}
public static TNode FromXml(string xmlText)
{
using (StringReader input = new StringReader(xmlText))
using (XmlTextReader xmlTextReader = new XmlTextReader(input) {
Normalization = false
}) {
xmlTextReader.MoveToContent();
TNode tNode = null;
TNode tNode2 = null;
Stack<TNode> stack = new Stack<TNode>();
while (!xmlTextReader.EOF) {
if (xmlTextReader.NodeType == XmlNodeType.Element) {
TNode item = tNode2;
tNode2 = new TNode(xmlTextReader.Name, xmlTextReader.Value);
if (tNode == null) {
tNode = tNode2;
stack.Push(tNode);
} else {
if (xmlTextReader.Depth > stack.Count)
stack.Push(item);
TNode tNode3 = stack.Peek();
tNode3.AddChildNode(tNode2);
}
int attributeCount = xmlTextReader.AttributeCount;
if (attributeCount > 0) {
for (int i = 0; i < attributeCount; i++) {
xmlTextReader.MoveToNextAttribute();
tNode2.AddAttribute(xmlTextReader.Name, xmlTextReader.Value);
}
}
} else if (xmlTextReader.NodeType == XmlNodeType.EndElement) {
if (xmlTextReader.Depth < stack.Count)
stack.Pop();
} else if (xmlTextReader.NodeType == XmlNodeType.Text) {
tNode2.Value = xmlTextReader.Value;
} else if (xmlTextReader.NodeType == XmlNodeType.CDATA) {
tNode2.Value = xmlTextReader.Value;
tNode2.ValueIsCDATA = true;
}
xmlTextReader.Read();
}
if (tNode == null)
throw new ArgumentException("Could not extract root element from " + xmlText);
return tNode;
}
}
public TNode AddElement(string name)
{
TNode tNode = new TNode(name);
AddChildNode(tNode);
return tNode;
}
public TNode AddElement(string name, string value)
{
TNode tNode = new TNode(name, value);
AddChildNode(tNode);
return tNode;
}
public TNode AddElementWithCDATA(string name, string value)
{
TNode tNode = new TNode(name, value, true);
AddChildNode(tNode);
return tNode;
}
public void AddChildNode(TNode node)
{
if (_childNodes == null)
_childNodes = new List<TNode>();
_childNodes.Add(node);
}
public void InsertChildNode(int index, TNode node)
{
if (_childNodes == null)
_childNodes = new List<TNode>();
_childNodes.Insert(index, node);
}
public void AddAttribute(string name, string value)
{
if (_attributes == null)
_attributes = new Dictionary<string, string>();
_attributes.Add(name, XmlExtensions.EscapeInvalidXmlCharacters(value));
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public TNode SelectSingleNode(string xpath)
{
List<TNode> list = SelectNodes(xpath);
if (list.Count <= 0)
return null;
return list[0];
}
public List<TNode> SelectNodes(string xpath)
{
List<TNode> list = new List<TNode>();
list.Add(this);
return ApplySelection(list, xpath);
}
public void WriteTo(XmlWriter writer)
{
writer.WriteStartElement(Name);
foreach (KeyValuePair<string, string> attribute in Attributes) {
writer.WriteAttributeString(attribute.Key, attribute.Value);
}
if (Value != null) {
if (ValueIsCDATA)
writer.WriteCDataSafe(Value);
else
writer.WriteString(Value);
}
NodeList childNodes = ChildNodes;
int count = childNodes.Count;
for (int i = 0; i < count; i++) {
childNodes = ChildNodes;
childNodes[i].WriteTo(writer);
}
writer.WriteEndElement();
}
private static TNode FromXml(XmlNode xmlNode)
{
TNode tNode = new TNode(xmlNode.Name, xmlNode.InnerText);
if (xmlNode.Attributes != null) {
foreach (XmlAttribute attribute in xmlNode.Attributes) {
tNode.AddAttribute(attribute.Name, attribute.Value);
}
}
foreach (XmlNode childNode in xmlNode.ChildNodes) {
if (childNode.NodeType == XmlNodeType.Element)
tNode.AddChildNode(FromXml(childNode));
}
return tNode;
}
private static List<TNode> ApplySelection(List<TNode> nodeList, string xpath)
{
Guard.ArgumentNotNullOrEmpty(xpath, "xpath");
if (xpath[0] == '/')
throw new ArgumentException("XPath expressions starting with '/' are not supported", "xpath");
if (xpath.IndexOf("//", StringComparison.Ordinal) >= 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);
}
List<TNode> list = new List<TNode>();
NodeFilter nodeFilter = new NodeFilter(xpath2);
int count = nodeList.Count;
for (int i = 0; i < count; i++) {
TNode tNode = nodeList[i];
NodeList childNodes = tNode.ChildNodes;
int count2 = childNodes.Count;
for (int j = 0; j < count2; j++) {
childNodes = tNode.ChildNodes;
TNode tNode2 = childNodes[j];
if (nodeFilter.Pass(tNode2))
list.Add(tNode2);
}
}
if (text == null)
return list;
return ApplySelection(list, text);
}
}
}