// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#nullable enable
using System;
using System.Globalization;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
namespace MS.Internal.Xml.Cache
{
private readonly Stack<XPathNodeRef> _stkNmsp; // In-scope namespaces
private XPathNodeInfoTable _infoTable; // Atomization table for shared node information
private XPathDocument _doc; // Currently building document
- private IXmlLineInfo _lineInfo; // Line information provider
+ private IXmlLineInfo? _lineInfo; // Line information provider
private XmlNameTable _nameTable; // Atomization table for all names in the document
private bool _atomizeNames; // True if all names should be atomized (false if they are pre-atomized)
private XPathNode[] _pageNmsp; // Page of last in-scope namespace node
private int _idxNmsp; // Page index of last in-scope namespace node
- private XPathNode[] _pageParent; // Page of last parent-type node (Element or Root)
+ private XPathNode[]? _pageParent; // Page of last parent-type node (Element or Root)
private int _idxParent; // Page index of last parent-type node (Element or Root)
- private XPathNode[] _pageSibling; // Page of previous sibling node (may be null if no previous sibling)
+ private XPathNode[]? _pageSibling; // Page of previous sibling node (may be null if no previous sibling)
private int _idxSibling; // Page index of previous sibling node
private int _lineNumBase; // Line number from which offsets are computed
private int _linePosBase; // Line position from which offsets are computed
- private XmlQualifiedName _idAttrName; // Cached name of an ID attribute
- private Hashtable _elemIdMap; // Map from element name to ID attribute name
+ private XmlQualifiedName? _idAttrName; // Cached name of an ID attribute
+ private Hashtable? _elemIdMap; // Map from element name to ID attribute name
private XPathNodeRef[] _elemNameIndex; // Elements with the same name are linked together so that they can be searched quickly
private const int ElementIndexSize = 64;
/// <summary>
/// Create a new XPathDocumentBuilder which creates nodes in "doc".
/// </summary>
- public XPathDocumentBuilder(XPathDocument doc, IXmlLineInfo lineInfo, string baseUri, XPathDocument.LoadFlags flags)
+ public XPathDocumentBuilder(XPathDocument doc, IXmlLineInfo? lineInfo, string? baseUri, XPathDocument.LoadFlags flags)
{
// Allocate the initial node (for non-namespaces) page, and the initial namespace page
_nodePageFact.Init(256);
/// Start construction of a new document. This must be called before any other methods are called.
/// It may also be called after Close(), in order to build further documents.
/// </summary>
- public void Initialize(XPathDocument doc, IXmlLineInfo lineInfo, string baseUri, XPathDocument.LoadFlags flags)
+ [MemberNotNull(nameof(_doc))]
+ [MemberNotNull(nameof(_elemNameIndex))]
+ [MemberNotNull(nameof(_infoTable))]
+ [MemberNotNull(nameof(_nameTable))]
+ [MemberNotNull(nameof(_pageNmsp))]
+ public void Initialize(XPathDocument doc, IXmlLineInfo? lineInfo, string? baseUri, XPathDocument.LoadFlags flags)
{
XPathNode[] page;
int idx;
/// <summary>
/// XPathDocument ignores the DocType information.
/// </summary>
- public override void WriteDocType(string name, string pubid, string sysid, string subset)
+ public override void WriteDocType(string name, string? pubid, string? sysid, string? subset)
{
}
/// <summary>
/// Shortcut for calling WriteStartElement with elemType == null.
/// </summary>
- public override void WriteStartElement(string prefix, string localName, string ns)
+ public override void WriteStartElement(string? prefix, string localName, string? ns)
{
this.WriteStartElement(prefix, localName, ns, string.Empty);
}
/// <summary>
/// Build an element node and attach it to its parent, if one exists. Make the element the new parent node.
/// </summary>
- public void WriteStartElement(string prefix, string localName, string ns, string baseUri)
+ public void WriteStartElement(string? prefix, string localName, string? ns, string? baseUri)
{
int hash;
Debug.Assert(prefix != null && localName != null && ns != null && localName.Length != 0 && baseUri != null);
// If elements within this document might have IDs, then cache the name of the ID attribute, if one exists
if (_elemIdMap != null)
- _idAttrName = (XmlQualifiedName)_elemIdMap[new XmlQualifiedName(localName, prefix)];
+ _idAttrName = (XmlQualifiedName?)_elemIdMap[new XmlQualifiedName(localName, prefix)];
}
/// <summary>
public void WriteEndElement(bool allowShortcutTag)
{
XPathNodeRef nodeRef;
- Debug.Assert(_pageParent[_idxParent].NodeType == XPathNodeType.Element);
+ Debug.Assert(_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element);
// If element has no content-typed children except for the one about to be added, then
// its value is the same as its only text child's.
/// <summary>
/// Shortcut for calling WriteStartAttribute with attrfType == null.
/// </summary>
- public override void WriteStartAttribute(string prefix, string localName, string namespaceName)
+ public override void WriteStartAttribute(string? prefix, string localName, string? namespaceName)
{
- Debug.Assert(!prefix.Equals("xmlns"));
- Debug.Assert(_idxParent == 0 || _pageParent[_idxParent].NodeType == XPathNodeType.Element);
- Debug.Assert(_idxSibling == 0 || _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute);
+ Debug.Assert(namespaceName != null);
+ Debug.Assert(prefix != null && !prefix.Equals("xmlns"));
+ Debug.Assert(_idxParent == 0 || (_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element));
+ Debug.Assert(_idxSibling == 0 || (_pageSibling != null && _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute));
if (_atomizeNames)
{
/// </summary>
public override void WriteEndAttribute()
{
+ Debug.Assert(_pageSibling != null);
Debug.Assert(_pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute);
_pageSibling[_idxSibling].SetValue(_textBldr.ReadText());
_pageSibling[_idxSibling].Prefix == _idAttrName.Namespace)
{
// Then add its value to the idValueMap map
- Debug.Assert(_idxParent != 0, "ID attribute must have an element parent");
- _doc.AddIdElement(_pageSibling[_idxSibling].Value, _pageParent, _idxParent);
+ Debug.Assert(_idxParent != 0 && _pageParent != null, "ID attribute must have an element parent");
+ string? id = _pageSibling[_idxSibling].Value;
+ Debug.Assert(id != null);
+ _doc.AddIdElement(id, _pageParent, _idxParent);
}
}
}
/// <summary>
/// Map CData text into regular text.
/// </summary>
- public override void WriteCData(string text)
+ public override void WriteCData(string? text)
{
WriteString(text, TextBlockType.Text);
}
/// <summary>
/// Construct comment node.
/// </summary>
- public override void WriteComment(string text)
+ public override void WriteComment(string? text)
{
AddSibling(XPathNodeType.Comment, string.Empty, string.Empty, string.Empty, string.Empty);
_pageSibling[_idxSibling].SetValue(text);
/// <summary>
/// Shortcut for calling WriteProcessingInstruction with baseUri = string.Empty.
/// </summary>
- public override void WriteProcessingInstruction(string name, string text)
+ public override void WriteProcessingInstruction(string name, string? text)
{
this.WriteProcessingInstruction(name, text, string.Empty);
}
/// <summary>
/// Construct pi node.
/// </summary>
- public void WriteProcessingInstruction(string name, string text, string baseUri)
+ public void WriteProcessingInstruction(string name, string? text, string? baseUri)
{
if (_atomizeNames)
name = _nameTable.Add(name);
/// <summary>
/// Write a whitespace text block.
/// </summary>
- public override void WriteWhitespace(string ws)
+ public override void WriteWhitespace(string? ws)
{
WriteString(ws, TextBlockType.Whitespace);
}
/// <summary>
/// Write an attribute or element text block.
/// </summary>
- public override void WriteString(string text)
+ public override void WriteString(string? text)
{
WriteString(text, TextBlockType.Text);
}
/// <summary>
/// Write an element text block with the specified text type (whitespace, significant whitespace, or text).
/// </summary>
- public void WriteString(string text, TextBlockType textType)
+ public void WriteString(string? text, TextBlockType textType)
{
_textBldr.WriteTextBlock(text, textType);
}
/// </summary>
public override void Close()
{
- XPathNode[] page;
+ XPathNode[]? page;
int idx;
// If cached text exists, then create a text node at the top-level
/// </summary>
internal override void StartElementContent()
{
- Debug.Assert(_pageParent[_idxParent].NodeType == XPathNodeType.Element);
+ Debug.Assert(_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element);
}
/// <summary>
/// </summary>
internal override void WriteNamespaceDeclaration(string prefix, string namespaceName)
{
- XPathNode[] pageTemp, pageOverride, pageNew, pageOrig, pageCopy;
+ XPathNode[] pageTemp, pageNew, pageCopy;
+ XPathNode[]? pageOverride, pageOrig;
int idxTemp, idxOverride, idxNew, idxOrig, idxCopy;
- Debug.Assert(_idxSibling == 0 || _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute);
+ Debug.Assert(_idxSibling == 0 || (_pageSibling != null && _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute));
Debug.Assert(!prefix.Equals("xmlns") && !namespaceName.Equals(XmlReservedNs.NsXmlNs));
Debug.Assert(_idxParent == 0 || _idxNmsp != 0);
- Debug.Assert(_idxParent == 0 || _pageParent[_idxParent].NodeType == XPathNodeType.Element);
+ Debug.Assert(_idxParent == 0 || (_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element));
if (_atomizeNames)
prefix = _nameTable.Add(prefix);
idxOverride = _idxNmsp;
while (idxOverride != 0)
{
- if ((object)pageOverride[idxOverride].LocalName == (object)prefix)
+ if ((object)pageOverride![idxOverride].LocalName == (object)prefix)
{
// Need to clone all namespaces up until the overridden node in order to bypass it
break;
while (idxOrig != idxOverride || pageOrig != pageOverride)
{
+ Debug.Assert(pageOrig != null);
// Make a copy of the original namespace node
- idxTemp = pageOrig[idxOrig].GetParent(out pageTemp);
+ idxTemp = pageOrig[idxOrig].GetParent(out pageTemp!);
idxTemp = NewNamespaceNode(out pageTemp, pageOrig[idxOrig].LocalName, pageOrig[idxOrig].Value, pageTemp, idxTemp);
// Attach copy to chain of copied nodes
}
// Link farther up in the original chain, just past the last overridden node
- idxOverride = pageOverride[idxOverride].GetSibling(out pageOverride);
+ idxOverride = pageOverride![idxOverride].GetSibling(out pageOverride);
if (idxOverride != 0)
- pageCopy[idxCopy].SetSibling(_infoTable, pageOverride, idxOverride);
+ pageCopy[idxCopy].SetSibling(_infoTable, pageOverride!, idxOverride);
else
Debug.Assert(prefix.Equals("xml"), "xmlns:xml namespace declaration should always be present in the list.");
}
if (_idxParent != 0)
{
+ Debug.Assert(_pageParent != null);
// If this is the first namespace on the current element,
if (!_pageParent[_idxParent].HasNamespaceDecls)
{
// Extract the elements which has attribute defined as ID from the element declarations
foreach (IDtdAttributeListInfo attrList in dtdInfo.GetAttributeLists())
{
- IDtdAttributeInfo idAttribute = attrList.LookupIdAttribute();
+ IDtdAttributeInfo? idAttribute = attrList.LookupIdAttribute();
if (idAttribute != null)
{
if (_elemIdMap == null)
/// <summary>
/// Helper method that constructs a new Namespace XPathNode.
/// </summary>
- private int NewNamespaceNode(out XPathNode[] page, string prefix, string namespaceUri, XPathNode[] pageElem, int idxElem)
+ private int NewNamespaceNode(out XPathNode[] page, string prefix, string? namespaceUri, XPathNode[]? pageElem, int idxElem)
{
XPathNode[] pageNode;
int idxNode, lineNumOffset, linePosOffset;
/// <summary>
/// Helper method that constructs a new XPathNode.
/// </summary>
- private int NewNode(out XPathNode[] page, XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string baseUri)
+ private int NewNode(out XPathNode[] page, XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string? baseUri)
{
XPathNode[] pageNode;
int idxNode, lineNumOffset, linePosOffset;
/// Add a sibling node. If no previous sibling exists, add the node as the first child of the parent.
/// If no parent exists, make this node the root of the document.
/// </summary>
- private void AddSibling(XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string baseUri)
+ [MemberNotNull(nameof(_pageSibling))]
+ private void AddSibling(XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string? baseUri)
{
XPathNode[] pageNew;
int idxNew;
// this.idxParent is only 0 for the top-most node
if (_idxParent != 0)
{
+ Debug.Assert(_pageParent != null);
// Set properties on parent
_pageParent[_idxParent].SetParentProperties(xptyp);
}
else
{
+ Debug.Assert(_pageSibling != null);
// There is already a previous sibling
_pageSibling[_idxSibling].SetSibling(_infoTable, pageNew, idxNew);
}
/// <summary>
/// Creates a text node from cached text parts.
/// </summary>
+ [MemberNotNull(nameof(_pageSibling))]
private void CachedTextNode()
{
TextBlockType textType;
string text;
Debug.Assert(_textBldr.HasText || (_idxSibling == 0 && _idxParent == 0), "Cannot create empty text node unless it's a top-level text node.");
- Debug.Assert(_idxSibling == 0 || !_pageSibling[_idxSibling].IsText, "Cannot create adjacent text nodes.");
+ Debug.Assert(_idxSibling == 0 || (_pageSibling != null && !_pageSibling[_idxSibling].IsText), "Cannot create adjacent text nodes.");
// Create a text node
textType = _textBldr.TextType;
/// </summary>
private struct TextBlockBuilder
{
- private IXmlLineInfo _lineInfo;
+ private IXmlLineInfo? _lineInfo;
private TextBlockType _textType;
private string _text;
private int _lineNum, _linePos;
/// <summary>
/// Constructor.
/// </summary>
- public void Initialize(IXmlLineInfo lineInfo)
+ public void Initialize(IXmlLineInfo? lineInfo)
{
_lineInfo = lineInfo;
_textType = TextBlockType.None;
/// <summary>
/// Append a text block with the specified type.
/// </summary>
- public void WriteTextBlock(string text, TextBlockType textType)
+ public void WriteTextBlock(string? text, TextBlockType textType)
{
Debug.Assert((int)XPathNodeType.Text < (int)XPathNodeType.SignificantWhitespace);
Debug.Assert((int)XPathNodeType.SignificantWhitespace < (int)XPathNodeType.Whitespace);
- if (text.Length != 0)
+ if (!string.IsNullOrEmpty(text))
{
if (_textType == TextBlockType.None)
{
{
get
{
- string value;
+ string? value;
XPathNode[] page;
XPathNode[]? pageEnd;
int idx, idxEnd;
if (_idxParent != 0)
{
Debug.Assert(_pageCurrent[_idxCurrent].NodeType == XPathNodeType.Text);
- return _pageParent![_idxParent].Value;
+
+ value = _pageParent![_idxParent].Value;
+ Debug.Assert(value != null);
+
+ return value;
}
// Must be node with complex content, so concatenate the string values of all text descendants
while (XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd))
{
Debug.Assert(page[idx].NodeType == XPathNodeType.Element || page[idx].IsText);
+ value = page[idx].Value;
+ Debug.Assert(value != null);
if (s.Length == 0)
{
- s = page[idx].Value;
+ s = value;
}
else
{
bldr = new StringBuilder();
bldr.Append(s);
}
- bldr.Append(page[idx].Value);
+ bldr.Append(value);
}
}
{
get
{
- XPathNode[] page;
+ XPathNode[]? page;
int idx;
if (_idxParent != 0)
do
{
- switch (page[idx].NodeType)
+ switch (page![idx].NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Root:
/// </summary>
public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope)
{
- XPathNode[] page;
+ XPathNode[]? page;
int idx;
if (namespaceScope == XPathNamespaceScope.Local)
while (idx != 0)
{
// Don't include the xmlns:xml namespace node if scope is ExcludeXml
- if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page[idx].IsXmlNamespaceNode)
+ if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page![idx].IsXmlNamespaceNode)
{
_pageParent = _pageCurrent;
_idxParent = _idxCurrent;
- _pageCurrent = page;
+ _pageCurrent = page!;
_idxCurrent = idx;
return true;
}
/// </summary>
public override bool MoveToNextNamespace(XPathNamespaceScope scope)
{
- XPathNode[] page = _pageCurrent, pageParent;
+ XPathNode[]? page = _pageCurrent, pageParent;
int idx = _idxCurrent, idxParent;
// If current node is not a namespace node, return false
if (idx == 0)
return false;
+ Debug.Assert(page != null);
switch (scope)
{
case XPathNamespaceScope.Local:
// Once parent changes, there are no longer any local namespaces
idxParent = page[idx].GetParent(out pageParent);
- if (idxParent != _idxParent || (object)pageParent != (object?)_pageParent)
+ if (idxParent != _idxParent || (object?)pageParent != (object?)_pageParent)
return false;
break;
// If this navigator is positioned on a virtual node, then compute following of parent
if (_idxParent != 0)
{
- if (!XPathNodeHelper.GetElementFollowing(ref _pageParent, ref _idxParent, pageEnd, idxEnd, _atomizedLocalName, namespaceURI))
+ if (!XPathNodeHelper.GetElementFollowing(ref _pageParent!, ref _idxParent, pageEnd, idxEnd, _atomizedLocalName, namespaceURI))
return false;
_pageCurrent = _pageParent;
// If this navigator is positioned on a virtual node, then compute following of parent
if (_idxParent != 0)
{
- if (!XPathNodeHelper.GetContentFollowing(ref _pageParent, ref _idxParent, pageEnd, idxEnd, type))
+ if (!XPathNodeHelper.GetContentFollowing(ref _pageParent!, ref _idxParent, pageEnd, idxEnd, type))
return false;
_pageCurrent = _pageParent;
XPathDocumentNavigator? that = other as XPathDocumentNavigator;
if (that != null)
{
- XPathNode[] pageThat;
+ XPathNode[]? pageThat;
int idxThat;
// If that current node's parent is virtualized, then start with the virtual parent
{
if (idxThat == _idxCurrent && pageThat == _pageCurrent)
return true;
- idxThat = pageThat[idxThat].GetParent(out pageThat);
+ idxThat = pageThat![idxThat].GetParent(out pageThat);
}
}
return false;
}
// Yes, so primary location should be derived from parent node
- return XPathNodeHelper.GetLocation(_pageParent, _idxParent);
+ return XPathNodeHelper.GetLocation(_pageParent!, _idxParent);
}
/// <summary>
// If the current node is virtualized, code its parent
if (_idxParent != 0)
{
- loc = (_pageParent![0].PageInfo.PageNumber - 1) << 16 | (_idxParent - 1);
+ loc = (_pageParent![0].PageInfo!.PageNumber - 1) << 16 | (_idxParent - 1);
do
{
buf[idx++] = UniqueIdTbl[loc & 0x1f];
}
// Code the node itself
- loc = (_pageCurrent[0].PageInfo.PageNumber - 1) << 16 | (_idxCurrent - 1);
+ loc = (_pageCurrent[0].PageInfo!.PageNumber - 1) << 16 | (_idxCurrent - 1);
do
{
buf[idx++] = UniqueIdTbl[loc & 0x1f];
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#nullable enable
using System;
using System.Diagnostics;
using System.Xml.XPath;
private ushort _idxSimilar; // Page index of next node in document order that has local name with same hashcode
private ushort _posOffset; // Line position offset of node (added to LinePositionBase)
private uint _props; // Node properties (broken down into bits below)
- private string _value; // String value of node
+ private string? _value; // String value of node
private const uint NodeTypeMask = 0xF;
private const uint HasAttributeBit = 0x10;
/// </summary>
public string BaseUri
{
- get { return _info.BaseUri; }
+ get { return _info.BaseUri ?? string.Empty; }
}
/// <summary>
/// <summary>
/// Returns information about the node page. Only the 0th node on each page has this property defined.
/// </summary>
- public XPathNodePageInfo PageInfo
+ public XPathNodePageInfo? PageInfo
{
get { return _info.PageInfo; }
}
/// </summary>
public int GetRoot(out XPathNode[] pageNode)
{
- return _info.Document.GetRootNode(out pageNode);
+ int idx = _info.Document.GetRootNode(out pageNode!);
+ Debug.Assert(pageNode != null);
+ return idx;
}
/// <summary>
/// Returns the parent of this node. If this node has no parent, then 0 is returned.
/// </summary>
- public int GetParent(out XPathNode[] pageNode)
+ public int GetParent(out XPathNode[]? pageNode)
{
pageNode = _info.ParentPage;
return _idxParent;
/// <summary>
/// Returns the next sibling of this node. If this node has no next sibling, then 0 is returned.
/// </summary>
- public int GetSibling(out XPathNode[] pageNode)
+ public int GetSibling(out XPathNode[]? pageNode)
{
pageNode = _info.SiblingPage;
return _idxSibling;
/// Returns the next element in document order that has the same local name hashcode as this element.
/// If there are no similar elements, then 0 is returned.
/// </summary>
- public int GetSimilarElement(out XPathNode[] pageNode)
+ public int GetSimilarElement(out XPathNode[]? pageNode)
{
pageNode = _info.SimilarElementPage;
return _idxSimilar;
/// Returns true if this node's name matches the specified localName and namespaceName. Assume
/// that localName has been atomized, but namespaceName has not.
/// </summary>
- public bool NameMatch(string localName, string namespaceName)
+ public bool NameMatch(string? localName, string namespaceName)
{
- Debug.Assert(localName == null || (object)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized.");
+ Debug.Assert(localName == null || (object?)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized.");
- return (object)_info.LocalName == (object)localName &&
+ return (object)_info.LocalName == (object?)localName &&
_info.NamespaceUri == namespaceName;
}
/// Returns true if this is an Element node with a name that matches the specified localName and
/// namespaceName. Assume that localName has been atomized, but namespaceName has not.
/// </summary>
- public bool ElementMatch(string localName, string namespaceName)
+ public bool ElementMatch(string? localName, string namespaceName)
{
- Debug.Assert(localName == null || (object)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized.");
+ Debug.Assert(localName == null || (object?)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized.");
return NodeType == XPathNodeType.Element &&
- (object)_info.LocalName == (object)localName &&
+ (object)_info.LocalName == (object?)localName &&
_info.NamespaceUri == namespaceName;
}
/// <summary>
/// Return the precomputed String value of this node (null if no value exists, i.e. document node, element node with complex content, etc).
/// </summary>
- public string Value
+ public string? Value
{
get { return _value; }
}
/// <summary>
/// Set this node's value.
/// </summary>
- public void SetValue(string value)
+ public void SetValue(string? value)
{
_value = value;
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#nullable enable
using System;
using System.Diagnostics;
using System.Xml.XPath;
/// parent is this node). Subsequent nodes may not have the same node as parent, so the caller will
/// need to test the parent in order to terminate a search that processes only local namespaces.
/// </summary>
- public static int GetLocalNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[] pageNmsp)
+ public static int GetLocalNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[]? pageNmsp)
{
if (pageElem[idxElem].HasNamespaceDecls)
{
/// have this element as their parent. Since the xmlns:xml namespace node is always in scope, this
/// method will never return 0 if the specified node is an element.
/// </summary>
- public static int GetInScopeNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[] pageNmsp)
+ public static int GetInScopeNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[]? pageNmsp)
{
XPathDocument doc;
// Walk ancestors, looking for an ancestor that has at least one namespace declaration
while (!pageElem[idxElem].HasNamespaceDecls)
{
- idxElem = pageElem[idxElem].GetParent(out pageElem);
+ idxElem = pageElem[idxElem].GetParent(out pageElem!);
if (idxElem == 0)
{
// There are no namespace nodes declared on ancestors, so return xmlns:xml node
/// </summary>
public static bool GetNextAttribute(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] page;
+ XPathNode[]? page;
int idx;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
idx = pageNode[idxNode].GetSibling(out page);
- if (idx != 0 && page[idx].NodeType == XPathNodeType.Attribute)
+ if (idx != 0 && page![idx].NodeType == XPathNodeType.Attribute)
{
pageNode = page;
idxNode = idx;
/// </summary>
public static bool GetContentChild(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
{
idx = page[idx].GetSibling(out page);
Debug.Assert(idx != 0);
+ Debug.Assert(page != null);
}
pageNode = page;
/// </summary>
public static bool GetContentSibling(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
idx = page[idx].GetSibling(out page);
if (idx != 0)
{
- pageNode = page;
+ pageNode = page!;
idxNode = idx;
return true;
}
/// </summary>
public static bool GetParent(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
idx = page[idx].GetParent(out page);
if (idx != 0)
{
- pageNode = page;
+ pageNode = page!;
idxNode = idx;
return true;
}
{
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
Debug.Assert(idxNode <= ushort.MaxValue);
- Debug.Assert(pageNode[0].PageInfo.PageNumber <= short.MaxValue);
- return (pageNode[0].PageInfo.PageNumber << 16) | idxNode;
+ Debug.Assert(pageNode[0].PageInfo!.PageNumber <= short.MaxValue);
+ return (pageNode[0].PageInfo!.PageNumber << 16) | idxNode;
}
/// <summary>
/// then do not set pageNode or idxNode and return false. Assume that the localName has been atomized with respect
/// to this document's name table, but not the namespaceName.
/// </summary>
- public static bool GetElementChild(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName)
+ public static bool GetElementChild(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
// Find element with specified localName and namespaceName
do
{
- if (page[idx].ElementMatch(localName, namespaceName))
+ if (page![idx].ElementMatch(localName, namespaceName))
{
pageNode = page;
idxNode = idx;
/// return false. Assume that the localName has been atomized with respect to this document's name table,
/// but not the namespaceName.
/// </summary>
- public static bool GetElementSibling(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName)
+ public static bool GetElementSibling(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
if (idx == 0)
break;
- if (page[idx].ElementMatch(localName, namespaceName))
+ if (page![idx].ElementMatch(localName, namespaceName))
{
pageNode = page;
idxNode = idx;
/// </summary>
public static bool GetContentChild(ref XPathNode[] pageNode, ref int idxNode, XPathNodeType typ)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
int mask;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
GetChild(ref page, ref idx);
do
{
- if (((1 << (int)page[idx].NodeType) & mask) != 0)
+ if (((1 << (int)page![idx].NodeType) & mask) != 0)
{
// Never return attributes, as Attribute is not a content type
if (typ == XPathNodeType.Attribute)
/// </summary>
public static bool GetContentSibling(ref XPathNode[] pageNode, ref int idxNode, XPathNodeType typ)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
int mask = XPathNavigator.GetContentKindMask(typ);
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
if (idx == 0)
break;
- if (((1 << (int)page[idx].NodeType) & mask) != 0)
+ if (((1 << (int)page![idx].NodeType) & mask) != 0)
{
Debug.Assert(typ != XPathNodeType.Attribute && typ != XPathNodeType.Namespace);
pageNode = page;
/// </summary>
public static bool GetPreviousContentSibling(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] pageParent = pageNode, pagePrec, pageAnc;
+ XPathNode[]? pageParent, pagePrec, pageAnc;
int idxParent = idxNode, idxPrec, idxAnc;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
Debug.Assert(pageNode[idxNode].NodeType != XPathNodeType.Attribute);
// 3. Get node that immediately precedes the current node in document order
// 4. If preceding node is parent, then there is no previous sibling, so return false
// 5. Walk ancestors of preceding node, until parent of current node is found
- idxParent = pageParent[idxParent].GetParent(out pageParent);
+ idxParent = pageNode[idxParent].GetParent(out pageParent);
if (idxParent != 0)
{
idxPrec = idxNode - 1;
if (idxPrec == 0)
{
// Need to get previous page
- pagePrec = pageNode[0].PageInfo.PreviousPage;
+ pagePrec = pageNode[0].PageInfo!.PreviousPage;
+ Debug.Assert(pagePrec != null);
idxPrec = pagePrec.Length - 1;
}
else
/// then do not set pageNode or idxNode and return false. Assume that the localName has been atomized with respect
/// to this document's name table, but not the namespaceName.
/// </summary>
- public static bool GetAttribute(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName)
+ public static bool GetAttribute(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)");
}
idx = page[idx].GetSibling(out page);
}
- while (idx != 0 && page[idx].NodeType == XPathNodeType.Attribute);
+ while (idx != 0 && page![idx].NodeType == XPathNodeType.Attribute);
}
return false;
/// If no such element exists, then do not set pageCurrent or idxCurrent and return false.
/// Assume that the localName has been atomized with respect to this document's name table, but not the namespaceName.
/// </summary>
- public static bool GetElementFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd, string localName, string namespaceName)
+ public static bool GetElementFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd, string? localName, string namespaceName)
{
- XPathNode[] page = pageCurrent;
+ XPathNode[]? page = pageCurrent;
int idx = idxCurrent;
Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)");
// If current node is an element having a matching name,
- if (page[idx].NodeType == XPathNodeType.Element && (object)page[idx].LocalName == (object)localName)
+ if (page[idx].NodeType == XPathNodeType.Element && (object)page[idx].LocalName == (object?)localName)
{
// Then follow similar element name pointers
int idxPageEnd = 0;
if (pageEnd != null)
{
- idxPageEnd = pageEnd[0].PageInfo.PageNumber;
- idxPageCurrent = page[0].PageInfo.PageNumber;
+ idxPageEnd = pageEnd[0].PageInfo!.PageNumber;
+ idxPageCurrent = page[0].PageInfo!.PageNumber;
// If ending node is <= starting node in document order, then scan to end of document
if (idxPageCurrent > idxPageEnd || (idxPageCurrent == idxPageEnd && idx >= idxEnd))
if (idx == 0)
break;
+ Debug.Assert(page != null);
+
// Only scan to ending node
if (pageEnd != null)
{
- idxPageCurrent = page[0].PageInfo.PageNumber;
+ idxPageCurrent = page[0].PageInfo!.PageNumber;
if (idxPageCurrent > idxPageEnd)
break;
idx++;
do
{
- if ((object)page == (object)pageEnd && idx <= idxEnd)
+ if ((object)page == (object?)pageEnd && idx <= idxEnd)
{
// Only scan to termination point
while (idx != idxEnd)
else
{
// Scan all nodes in the page
- while (idx < page[0].PageInfo.NodeCount)
+ while (idx < page[0].PageInfo!.NodeCount)
{
if (page[idx].ElementMatch(localName, namespaceName))
goto FoundNode;
}
}
- page = page[0].PageInfo.NextPage;
+ page = page[0].PageInfo!.NextPage;
idx = 1;
}
while (page != null);
/// 3. Has the specified XPathNodeType (but Attributes and Namespaces never match)
/// If no such node exists, then do not set pageCurrent or idxCurrent and return false.
/// </summary>
- public static bool GetContentFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd, XPathNodeType typ)
+ public static bool GetContentFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd, XPathNodeType typ)
{
- XPathNode[] page = pageCurrent;
+ XPathNode[]? page = pageCurrent;
int idx = idxCurrent;
int mask = XPathNavigator.GetContentKindMask(typ);
Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)");
idx++;
do
{
- if ((object)page == (object)pageEnd && idx <= idxEnd)
+ if ((object)page == (object?)pageEnd && idx <= idxEnd)
{
// Only scan to termination point
while (idx != idxEnd)
else
{
// Scan all nodes in the page
- while (idx < page[0].PageInfo.NodeCount)
+ while (idx < page[0].PageInfo!.NodeCount)
{
if (((1 << (int)page[idx].NodeType) & mask) != 0)
goto FoundNode;
}
}
- page = page[0].PageInfo.NextPage;
+ page = page[0].PageInfo!.NextPage;
idx = 1;
}
while (page != null);
/// 2. Non-collapsed text nodes
/// If no such node exists, then do not set pageCurrent or idxCurrent and return false.
/// </summary>
- public static bool GetTextFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd)
+ public static bool GetTextFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd)
{
- XPathNode[] page = pageCurrent;
+ XPathNode[]? page = pageCurrent;
int idx = idxCurrent;
Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)");
Debug.Assert(!page[idx].IsAttrNmsp, "Current node should never be an attribute or namespace--caller should handle this case.");
idx++;
do
{
- if ((object)page == (object)pageEnd && idx <= idxEnd)
+ if ((object)page == (object?)pageEnd && idx <= idxEnd)
{
// Only scan to termination point
while (idx != idxEnd)
else
{
// Scan all nodes in the page
- while (idx < page[0].PageInfo.NodeCount)
+ while (idx < page[0].PageInfo!.NodeCount)
{
if (page[idx].IsText || (page[idx].NodeType == XPathNodeType.Element && page[idx].HasCollapsedText))
goto FoundNode;
}
}
- page = page[0].PageInfo.NextPage;
+ page = page[0].PageInfo!.NextPage;
idx = 1;
}
while (page != null);
/// </summary>
public static bool GetNonDescendant(ref XPathNode[] pageNode, ref int idxNode)
{
- XPathNode[] page = pageNode;
+ XPathNode[]? page = pageNode;
int idx = idxNode;
// Get page, idx at which to end sequential scan of nodes
do
{
// If the current node has a sibling,
- if (page[idx].HasSibling)
+ if (page![idx].HasSibling)
{
// Then that is the first non-descendant
pageNode = page;
- idxNode = page[idx].GetSibling(out pageNode);
+ idxNode = page[idx].GetSibling(out pageNode!);
return true;
}
if (++idxNode >= pageNode.Length)
{
// Child is first node on next page
- pageNode = pageNode[0].PageInfo.NextPage;
+ pageNode = pageNode[0].PageInfo!.NextPage!;
+ Debug.Assert(pageNode != null);
idxNode = 1;
}
// Else child is next node on this page
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#nullable enable
using System;
using System.Diagnostics;
using System.Text;
{
private readonly int _pageNum;
private int _nodeCount;
- private readonly XPathNode[] _pagePrev;
- private XPathNode[] _pageNext;
+ private readonly XPathNode[]? _pagePrev;
+ private XPathNode[]? _pageNext;
/// <summary>
/// Constructor.
/// </summary>
- public XPathNodePageInfo(XPathNode[] pagePrev, int pageNum)
+ public XPathNodePageInfo(XPathNode[]? pagePrev, int pageNum)
{
_pagePrev = pagePrev;
_pageNum = pageNum;
/// <summary>
/// Return the previous node page in the document.
/// </summary>
- public XPathNode[] PreviousPage
+ public XPathNode[]? PreviousPage
{
get { return _pagePrev; }
}
/// <summary>
/// Return the next node page in the document.
/// </summary>
- public XPathNode[] NextPage
+ public XPathNode[]? NextPage
{
get { return _pageNext; }
set { _pageNext = value; }
/// </summary>
internal sealed class XPathNodeInfoAtom : IEquatable<XPathNodeInfoAtom>
{
- private string _localName;
- private string _namespaceUri;
- private string _prefix;
- private string _baseUri;
- private XPathNode[] _pageParent;
- private XPathNode[] _pageSibling;
- private XPathNode[] _pageSimilar;
- private XPathDocument _doc;
+ private string _localName = null!;
+ private string _namespaceUri = null!;
+ private string _prefix = null!;
+ private string? _baseUri;
+ private XPathNode[]? _pageParent;
+ private XPathNode[]? _pageSibling;
+ private XPathNode[]? _pageSimilar;
+ private XPathDocument _doc = null!;
private int _lineNumBase;
private int _linePosBase;
private int _hashCode;
private int _localNameHash;
- private XPathNodeInfoAtom _next;
- private XPathNodePageInfo _pageInfo;
+ private XPathNodeInfoAtom? _next;
+ private XPathNodePageInfo? _pageInfo;
/// <summary>
/// <summary>
/// Construct a new shared information atom. This method should only be used by the XNodeInfoTable.
/// </summary>
- public XPathNodeInfoAtom(string localName, string namespaceUri, string prefix, string baseUri,
- XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar,
+ public XPathNodeInfoAtom(string localName, string namespaceUri, string prefix, string? baseUri,
+ XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar,
XPathDocument doc, int lineNumBase, int linePosBase)
{
Init(localName, namespaceUri, prefix, baseUri, pageParent, pageSibling, pageSimilar, doc, lineNumBase, linePosBase);
/// <summary>
/// Initialize an existing shared information atom. This method should only be used by the XNodeInfoTable.
/// </summary>
- public void Init(string localName, string namespaceUri, string prefix, string baseUri,
- XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar,
+ public void Init(string localName, string namespaceUri, string prefix, string? baseUri,
+ XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar,
XPathDocument doc, int lineNumBase, int linePosBase)
{
Debug.Assert(localName != null && namespaceUri != null && prefix != null && doc != null);
/// <summary>
/// Returns information about the node page. Only the 0th node on each page has this property defined.
/// </summary>
- public XPathNodePageInfo PageInfo
+ public XPathNodePageInfo? PageInfo
{
get { return _pageInfo; }
}
/// <summary>
/// Return the base Uri of nodes that share this information atom.
/// </summary>
- public string BaseUri
+ public string? BaseUri
{
get { return _baseUri; }
}
/// <summary>
/// Return the page containing the next sibling of nodes that share this information atom.
/// </summary>
- public XPathNode[] SiblingPage
+ public XPathNode[]? SiblingPage
{
get { return _pageSibling; }
}
/// <summary>
/// Return the page containing the next element having a name which has same hashcode as this element.
/// </summary>
- public XPathNode[] SimilarElementPage
+ public XPathNode[]? SimilarElementPage
{
get { return _pageSimilar; }
}
/// <summary>
/// Return the page containing the parent of nodes that share this information atom.
/// </summary>
- public XPathNode[] ParentPage
+ public XPathNode[]? ParentPage
{
get { return _pageParent; }
}
/// <summary>
/// Link together InfoAtoms that hash to the same hashtable bucket (should only be used by XPathNodeInfoTable)
/// </summary>
- public XPathNodeInfoAtom Next
+ public XPathNodeInfoAtom? Next
{
get { return _next; }
set { _next = value; }
unchecked
{
if (_pageSibling != null)
- hashCode += (hashCode << 7) ^ _pageSibling[0].PageInfo.PageNumber;
+ hashCode += (hashCode << 7) ^ _pageSibling[0].PageInfo!.PageNumber;
if (_pageParent != null)
- hashCode += (hashCode << 7) ^ _pageParent[0].PageInfo.PageNumber;
+ hashCode += (hashCode << 7) ^ _pageParent[0].PageInfo!.PageNumber;
if (_pageSimilar != null)
- hashCode += (hashCode << 7) ^ _pageSimilar[0].PageInfo.PageNumber;
+ hashCode += (hashCode << 7) ^ _pageSimilar[0].PageInfo!.PageNumber;
}
// Save hashcode. Don't save 0, so that it won't ever be recomputed.
/// <summary>
/// Return true if this InfoAtom has the same values as another InfoAtom.
/// </summary>
- public override bool Equals(object other)
+ public override bool Equals(object? other)
{
return Equals(other as XPathNodeInfoAtom);
}
- public bool Equals(XPathNodeInfoAtom other)
+ public bool Equals(XPathNodeInfoAtom? other)
{
Debug.Assert(other != null);
- Debug.Assert((object)_doc == (object)other._doc);
+ Debug.Assert((object?)_doc == (object?)other._doc);
Debug.Assert(_pageInfo == null);
// Assume that name parts are atomized
if (this.GetHashCode() == other.GetHashCode())
{
- if ((object)_localName == (object)other._localName &&
- (object)_pageSibling == (object)other._pageSibling &&
- (object)_namespaceUri == (object)other._namespaceUri &&
- (object)_pageParent == (object)other._pageParent &&
- (object)_pageSimilar == (object)other._pageSimilar &&
- (object)_prefix == (object)other._prefix &&
- (object)_baseUri == (object)other._baseUri &&
+ if ((object?)_localName == (object?)other._localName &&
+ (object?)_pageSibling == (object?)other._pageSibling &&
+ (object?)_namespaceUri == (object?)other._namespaceUri &&
+ (object?)_pageParent == (object?)other._pageParent &&
+ (object?)_pageSimilar == (object?)other._pageSimilar &&
+ (object?)_prefix == (object?)other._prefix &&
+ (object?)_baseUri == (object?)other._baseUri &&
_lineNumBase == other._lineNumBase &&
_linePosBase == other._linePosBase)
{
bldr.Append(GetHashCode());
bldr.Append(", ");
+ Debug.Assert(_localName != null);
if (_localName.Length != 0)
{
bldr.Append('{');
bldr.Append(_namespaceUri);
bldr.Append('}');
+ Debug.Assert(_prefix != null);
if (_prefix.Length != 0)
{
bldr.Append(_prefix);
if (_pageParent != null)
{
bldr.Append("parent=");
- bldr.Append(_pageParent[0].PageInfo.PageNumber);
+ bldr.Append(_pageParent[0].PageInfo!.PageNumber);
bldr.Append(", ");
}
if (_pageSibling != null)
{
bldr.Append("sibling=");
- bldr.Append(_pageSibling[0].PageInfo.PageNumber);
+ bldr.Append(_pageSibling[0].PageInfo!.PageNumber);
bldr.Append(", ");
}
if (_pageSimilar != null)
{
bldr.Append("similar=");
- bldr.Append(_pageSimilar[0].PageInfo.PageNumber);
+ bldr.Append(_pageSimilar[0].PageInfo!.PageNumber);
bldr.Append(", ");
}
{
private XPathNodeInfoAtom[] _hashTable;
private int _sizeTable;
- private XPathNodeInfoAtom _infoCached;
+ private XPathNodeInfoAtom? _infoCached;
#if DEBUG
private const int DefaultTableSize = 2;
/// <summary>
/// Create a new XNodeInfoAtom and ensure it is atomized in the table.
/// </summary>
- public XPathNodeInfoAtom Create(string localName, string namespaceUri, string prefix, string baseUri,
- XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar,
+ public XPathNodeInfoAtom Create(string localName, string namespaceUri, string prefix, string? baseUri,
+ XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar,
XPathDocument doc, int lineNumBase, int linePosBase)
{
XPathNodeInfoAtom info;
/// </summary>
private XPathNodeInfoAtom Atomize(XPathNodeInfoAtom info)
{
- XPathNodeInfoAtom infoNew, infoNext;
+ XPathNodeInfoAtom? infoNew, infoNext;
// Search for existing XNodeInfoAtom in the table
infoNew = _hashTable[info.GetHashCode() & (_hashTable.Length - 1)];
public override string ToString()
{
StringBuilder bldr = new StringBuilder();
- XPathNodeInfoAtom infoAtom;
+ XPathNodeInfoAtom? infoAtom;
for (int i = 0; i < _hashTable.Length; i++)
{