File: System\Xml\Linq\XNodeReader.cs
Web Access
Project: src\src\libraries\System.Private.Xml.Linq\src\System.Private.Xml.Linq.csproj (System.Private.Xml.Linq)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Debug = System.Diagnostics.Debug;
 
namespace System.Xml.Linq
{
    internal sealed class XNodeReader : XmlReader, IXmlLineInfo
    {
        // The reader position is encoded by the tuple (source, parent).
        // Lazy text uses (instance, parent element). Attribute value
        // uses (instance, parent attribute). End element uses (instance,
        // instance). Common XObject uses (instance, null).
        private object _source;
        private object? _parent;
        private ReadState _state;
        private XNode _root;
        private readonly XmlNameTable _nameTable;
        private readonly bool _omitDuplicateNamespaces;
 
        internal XNodeReader(XNode node, XmlNameTable? nameTable, ReaderOptions options)
        {
            _source = node;
            _root = node;
            _nameTable = nameTable ?? CreateNameTable();
            _omitDuplicateNamespaces = (options & ReaderOptions.OmitDuplicateNamespaces) != 0 ? true : false;
        }
 
        internal XNodeReader(XNode node, XmlNameTable? nameTable)
            : this(node, nameTable,
                (node.GetSaveOptionsFromAnnotations() & SaveOptions.OmitDuplicateNamespaces) != 0 ?
                    ReaderOptions.OmitDuplicateNamespaces : ReaderOptions.None)
        {
        }
 
        public override int AttributeCount
        {
            get
            {
                if (!IsInteractive)
                {
                    return 0;
                }
                int count = 0;
                XElement? e = GetElementInAttributeScope();
                if (e != null)
                {
                    XAttribute? a = e.lastAttr;
                    if (a != null)
                    {
                        do
                        {
                            a = a.next!;
                            if (!_omitDuplicateNamespaces || !IsDuplicateNamespaceAttribute(a))
                            {
                                count++;
                            }
                        } while (a != e.lastAttr);
                    }
                }
                return count;
            }
        }
 
        public override string BaseURI
        {
            get
            {
                XObject? o = _source as XObject;
                if (o != null)
                {
                    return o.BaseUri;
                }
                o = _parent as XObject;
                if (o != null)
                {
                    return o.BaseUri;
                }
                return string.Empty;
            }
        }
 
        public override int Depth
        {
            get
            {
                if (!IsInteractive)
                {
                    return 0;
                }
                XObject? o = _source as XObject;
                if (o != null)
                {
                    return GetDepth(o);
                }
                o = _parent as XObject;
                if (o != null)
                {
                    return GetDepth(o) + 1;
                }
                return 0;
            }
        }
 
        private static int GetDepth(XObject o)
        {
            int depth = 0;
            while (o.parent != null)
            {
                depth++;
                o = o.parent;
            }
            if (o is XDocument)
            {
                depth--;
            }
            return depth;
        }
 
        public override bool EOF
        {
            get { return _state == ReadState.EndOfFile; }
        }
 
        public override bool HasAttributes
        {
            get
            {
                if (!IsInteractive)
                {
                    return false;
                }
                XElement? e = GetElementInAttributeScope();
                if (e != null && e.lastAttr != null)
                {
                    if (_omitDuplicateNamespaces)
                    {
                        return GetFirstNonDuplicateNamespaceAttribute(e.lastAttr.next!) != null;
                    }
                    else
                    {
                        return true;
                    }
                }
                else
                {
                    return false;
                }
            }
        }
 
        public override bool HasValue
        {
            get
            {
                if (!IsInteractive)
                {
                    return false;
                }
                XObject? o = _source as XObject;
                if (o != null)
                {
                    switch (o.NodeType)
                    {
                        case XmlNodeType.Attribute:
                        case XmlNodeType.Text:
                        case XmlNodeType.CDATA:
                        case XmlNodeType.Comment:
                        case XmlNodeType.ProcessingInstruction:
                        case XmlNodeType.DocumentType:
                            return true;
                        default:
                            return false;
                    }
                }
                return true;
            }
        }
 
        public override bool IsEmptyElement
        {
            get
            {
                if (!IsInteractive)
                {
                    return false;
                }
                XElement? e = _source as XElement;
                return e != null && e.IsEmpty;
            }
        }
 
        public override string LocalName
        {
            get { return _nameTable.Add(GetLocalName()); }
        }
 
        private string GetLocalName()
        {
            if (!IsInteractive)
            {
                return string.Empty;
            }
            XElement? e = _source as XElement;
            if (e != null)
            {
                return e.Name.LocalName;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                return a.Name.LocalName;
            }
            XProcessingInstruction? p = _source as XProcessingInstruction;
            if (p != null)
            {
                return p.Target;
            }
            XDocumentType? n = _source as XDocumentType;
            if (n != null)
            {
                return n.Name;
            }
            return string.Empty;
        }
 
        public override string Name
        {
            get
            {
                string prefix = GetPrefix();
                if (prefix.Length == 0)
                {
                    return _nameTable.Add(GetLocalName());
                }
                return _nameTable.Add(string.Concat(prefix, ":", GetLocalName()));
            }
        }
 
        public override string NamespaceURI
        {
            get { return _nameTable.Add(GetNamespaceURI()); }
        }
 
        private string GetNamespaceURI()
        {
            if (!IsInteractive)
            {
                return string.Empty;
            }
            XElement? e = _source as XElement;
            if (e != null)
            {
                return e.Name.NamespaceName;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                string namespaceName = a.Name.NamespaceName;
                if (namespaceName.Length == 0 && a.Name.LocalName == "xmlns")
                {
                    return XNamespace.xmlnsPrefixNamespace;
                }
                return namespaceName;
            }
            return string.Empty;
        }
 
        public override XmlNameTable NameTable
        {
            get { return _nameTable; }
        }
 
        public override XmlNodeType NodeType
        {
            get
            {
                if (!IsInteractive)
                {
                    return XmlNodeType.None;
                }
                XObject? o = _source as XObject;
                if (o != null)
                {
                    if (IsEndElement)
                    {
                        return XmlNodeType.EndElement;
                    }
                    XmlNodeType nt = o.NodeType;
                    if (nt != XmlNodeType.Text)
                    {
                        return nt;
                    }
                    if (o.parent != null && o.parent.parent == null && o.parent is XDocument)
                    {
                        return XmlNodeType.Whitespace;
                    }
                    return XmlNodeType.Text;
                }
                if (_parent is XDocument)
                {
                    return XmlNodeType.Whitespace;
                }
                return XmlNodeType.Text;
            }
        }
 
        public override string Prefix
        {
            get { return _nameTable.Add(GetPrefix()); }
        }
 
        private string GetPrefix()
        {
            if (!IsInteractive)
            {
                return string.Empty;
            }
            XElement? e = _source as XElement;
            if (e != null)
            {
                string? prefix = e.GetPrefixOfNamespace(e.Name.Namespace);
                if (prefix != null)
                {
                    return prefix;
                }
                return string.Empty;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                string? prefix = a.GetPrefixOfNamespace(a.Name.Namespace);
                if (prefix != null)
                {
                    return prefix;
                }
            }
            return string.Empty;
        }
 
        public override ReadState ReadState
        {
            get { return _state; }
        }
 
        public override XmlReaderSettings Settings
        {
            get
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.CheckCharacters = false;
                return settings;
            }
        }
 
        public override string Value
        {
            get
            {
                if (!IsInteractive)
                {
                    return string.Empty;
                }
                XObject? o = _source as XObject;
                if (o != null)
                {
                    switch (o.NodeType)
                    {
                        case XmlNodeType.Attribute:
                            return ((XAttribute)o).Value;
                        case XmlNodeType.Text:
                        case XmlNodeType.CDATA:
                            return ((XText)o).Value;
                        case XmlNodeType.Comment:
                            return ((XComment)o).Value;
                        case XmlNodeType.ProcessingInstruction:
                            return ((XProcessingInstruction)o).Data;
                        case XmlNodeType.DocumentType:
                            return ((XDocumentType)o).InternalSubset ?? string.Empty;
                        default:
                            return string.Empty;
                    }
                }
                return (string)_source;
            }
        }
 
        public override string XmlLang
        {
            get
            {
                if (!IsInteractive)
                {
                    return string.Empty;
                }
                XElement? e = GetElementInScope();
                if (e != null)
                {
                    XName name = XNamespace.Xml.GetName("lang");
                    do
                    {
                        XAttribute? a = e.Attribute(name);
                        if (a != null)
                        {
                            return a.Value;
                        }
                        e = e.parent as XElement;
                    } while (e != null);
                }
                return string.Empty;
            }
        }
 
        public override XmlSpace XmlSpace
        {
            get
            {
                if (!IsInteractive)
                {
                    return XmlSpace.None;
                }
                XElement? e = GetElementInScope();
                if (e != null)
                {
                    XName name = XNamespace.Xml.GetName("space");
                    do
                    {
                        XAttribute? a = e.Attribute(name);
                        if (a != null)
                        {
                            switch (a.Value.AsSpan().Trim(" \t\n\r"))
                            {
                                case "preserve":
                                    return XmlSpace.Preserve;
                                case "default":
                                    return XmlSpace.Default;
                                default:
                                    break;
                            }
                        }
                        e = e.parent as XElement;
                    } while (e != null);
                }
                return XmlSpace.None;
            }
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing && ReadState != ReadState.Closed)
            {
                Close();
            }
        }
 
        public override void Close()
        {
            _source = null!;
            _parent = null;
            _root = null!;
            _state = ReadState.Closed;
        }
 
        public override string? GetAttribute(string name)
        {
            if (!IsInteractive)
            {
                return null;
            }
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                string? localName, namespaceName;
                GetNameInAttributeScope(name, e, out localName, out namespaceName);
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (a.Name.LocalName == localName && a.Name.NamespaceName == namespaceName)
                        {
                            if (_omitDuplicateNamespaces && IsDuplicateNamespaceAttribute(a))
                            {
                                return null;
                            }
                            else
                            {
                                return a.Value;
                            }
                        }
                    } while (a != e.lastAttr);
                }
                return null;
            }
            XDocumentType? n = _source as XDocumentType;
            if (n != null)
            {
                switch (name)
                {
                    case "PUBLIC":
                        return n.PublicId;
                    case "SYSTEM":
                        return n.SystemId;
                }
            }
            return null;
        }
 
        public override string? GetAttribute(string localName, string? namespaceName)
        {
            if (!IsInteractive)
            {
                return null;
            }
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                if (localName == "xmlns")
                {
                    if (namespaceName != null && namespaceName.Length == 0)
                    {
                        return null;
                    }
                    if (namespaceName == XNamespace.xmlnsPrefixNamespace)
                    {
                        namespaceName = string.Empty;
                    }
                }
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (a.Name.LocalName == localName && a.Name.NamespaceName == namespaceName)
                        {
                            if (_omitDuplicateNamespaces && IsDuplicateNamespaceAttribute(a))
                            {
                                return null;
                            }
                            else
                            {
                                return a.Value;
                            }
                        }
                    } while (a != e.lastAttr);
                }
            }
            return null;
        }
 
        public override string GetAttribute(int index)
        {
            if (!IsInteractive)
            {
                throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive);
            }
 
            ArgumentOutOfRangeException.ThrowIfNegative(index);
 
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (!_omitDuplicateNamespaces || !IsDuplicateNamespaceAttribute(a))
                        {
                            if (index-- == 0)
                            {
                                return a.Value;
                            }
                        }
                    } while (a != e.lastAttr);
                }
            }
            throw new ArgumentOutOfRangeException(nameof(index));
        }
 
        public override string? LookupNamespace(string prefix)
        {
            if (!IsInteractive)
            {
                return null;
            }
            if (prefix == null)
            {
                return null;
            }
            XElement? e = GetElementInScope();
            if (e != null)
            {
                XNamespace? ns = prefix.Length == 0 ? e.GetDefaultNamespace() : e.GetNamespaceOfPrefix(prefix);
                if (ns != null)
                {
                    return _nameTable.Add(ns.NamespaceName);
                }
            }
            return null;
        }
 
        public override bool MoveToAttribute(string name)
        {
            if (!IsInteractive)
            {
                return false;
            }
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                string? localName, namespaceName;
                GetNameInAttributeScope(name, e, out localName, out namespaceName);
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (a.Name.LocalName == localName &&
                            a.Name.NamespaceName == namespaceName)
                        {
                            if (_omitDuplicateNamespaces && IsDuplicateNamespaceAttribute(a))
                            {
                                // If it's a duplicate namespace attribute just act as if it doesn't exist
                                return false;
                            }
                            else
                            {
                                _source = a;
                                _parent = null;
                                return true;
                            }
                        }
                    } while (a != e.lastAttr);
                }
            }
            return false;
        }
 
        public override bool MoveToAttribute(string localName, string? namespaceName)
        {
            if (!IsInteractive)
            {
                return false;
            }
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                if (localName == "xmlns")
                {
                    if (namespaceName != null && namespaceName.Length == 0)
                    {
                        return false;
                    }
                    if (namespaceName == XNamespace.xmlnsPrefixNamespace)
                    {
                        namespaceName = string.Empty;
                    }
                }
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (a.Name.LocalName == localName &&
                            a.Name.NamespaceName == namespaceName)
                        {
                            if (_omitDuplicateNamespaces && IsDuplicateNamespaceAttribute(a))
                            {
                                // If it's a duplicate namespace attribute just act as if it doesn't exist
                                return false;
                            }
                            else
                            {
                                _source = a;
                                _parent = null;
                                return true;
                            }
                        }
                    } while (a != e.lastAttr);
                }
            }
            return false;
        }
 
        public override void MoveToAttribute(int index)
        {
            if (!IsInteractive)
            {
                return;
            }
            ArgumentOutOfRangeException.ThrowIfNegative(index);
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (!_omitDuplicateNamespaces || !IsDuplicateNamespaceAttribute(a))
                        {
                            // Only count those which are non-duplicates if we're asked to
                            if (index-- == 0)
                            {
                                _source = a;
                                _parent = null;
                                return;
                            }
                        }
                    } while (a != e.lastAttr);
                }
            }
            throw new ArgumentOutOfRangeException(nameof(index));
        }
 
        public override bool MoveToElement()
        {
            if (!IsInteractive)
            {
                return false;
            }
 
            XAttribute? a =
                _source as XAttribute ??
                _parent as XAttribute;
 
            if (a != null)
            {
                if (a.parent != null)
                {
                    _source = a.parent;
                    _parent = null;
                    return true;
                }
            }
            return false;
        }
 
        public override bool MoveToFirstAttribute()
        {
            if (!IsInteractive)
            {
                return false;
            }
            XElement? e = GetElementInAttributeScope();
            if (e != null)
            {
                if (e.lastAttr != null)
                {
                    if (_omitDuplicateNamespaces)
                    {
                        object? na = GetFirstNonDuplicateNamespaceAttribute(e.lastAttr.next!);
                        if (na == null)
                        {
                            return false;
                        }
                        _source = na;
                    }
                    else
                    {
                        _source = e.lastAttr.next!;
                    }
                    return true;
                }
            }
            return false;
        }
 
        public override bool MoveToNextAttribute()
        {
            if (!IsInteractive)
            {
                return false;
            }
            XElement? e = _source as XElement;
            if (e != null)
            {
                if (IsEndElement)
                {
                    return false;
                }
                if (e.lastAttr != null)
                {
                    if (_omitDuplicateNamespaces)
                    {
                        // Skip duplicate namespace attributes
                        // We must NOT modify the this.source until we find the one we're looking for
                        //   because if we don't find anything, we need to stay positioned where we're now
                        object? na = GetFirstNonDuplicateNamespaceAttribute(e.lastAttr.next!);
                        if (na == null)
                        {
                            return false;
                        }
                        _source = na;
                    }
                    else
                    {
                        _source = e.lastAttr.next!;
                    }
                    return true;
                }
                return false;
            }
 
            XAttribute? a =
                _source as XAttribute ??
                _parent as XAttribute;
 
            if (a != null)
            {
                if (a.parent != null && ((XElement)a.parent).lastAttr != a)
                {
                    if (_omitDuplicateNamespaces)
                    {
                        // Skip duplicate namespace attributes
                        // We must NOT modify the this.source until we find the one we're looking for
                        //   because if we don't find anything, we need to stay positioned where we're now
                        object? na = GetFirstNonDuplicateNamespaceAttribute(a.next!);
                        if (na == null)
                        {
                            return false;
                        }
                        _source = na;
                    }
                    else
                    {
                        _source = a.next!;
                    }
                    _parent = null;
                    return true;
                }
            }
            return false;
        }
 
        public override bool Read()
        {
            switch (_state)
            {
                case ReadState.Initial:
                    _state = ReadState.Interactive;
                    XDocument? d = _source as XDocument;
                    if (d != null)
                    {
                        return ReadIntoDocument(d);
                    }
                    return true;
                case ReadState.Interactive:
                    return Read(false);
                default:
                    return false;
            }
        }
 
        public override bool ReadAttributeValue()
        {
            if (!IsInteractive)
            {
                return false;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                return ReadIntoAttribute(a);
            }
            return false;
        }
 
        public override bool ReadToDescendant(string localName, string namespaceName)
        {
            if (!IsInteractive)
            {
                return false;
            }
            MoveToElement();
            XElement? c = _source as XElement;
            if (c != null && !c.IsEmpty)
            {
                if (IsEndElement)
                {
                    return false;
                }
                foreach (XElement e in c.Descendants())
                {
                    if (e.Name.LocalName == localName &&
                        e.Name.NamespaceName == namespaceName)
                    {
                        _source = e;
                        return true;
                    }
                }
                IsEndElement = true;
            }
            return false;
        }
 
        public override bool ReadToFollowing(string localName, string namespaceName)
        {
            while (Read())
            {
                XElement? e = _source as XElement;
                if (e != null)
                {
                    if (IsEndElement) continue;
                    if (e.Name.LocalName == localName && e.Name.NamespaceName == namespaceName)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
 
        public override bool ReadToNextSibling(string localName, string namespaceName)
        {
            if (!IsInteractive)
            {
                return false;
            }
            MoveToElement();
            if (_source != _root)
            {
                XNode? n = _source as XNode;
                if (n != null)
                {
                    foreach (XElement e in n.ElementsAfterSelf())
                    {
                        if (e.Name.LocalName == localName &&
                            e.Name.NamespaceName == namespaceName)
                        {
                            _source = e;
                            IsEndElement = false;
                            return true;
                        }
                    }
                    if (n.parent is XElement)
                    {
                        _source = n.parent;
                        IsEndElement = true;
                        return false;
                    }
                }
                else
                {
                    if (_parent is XElement)
                    {
                        _source = _parent;
                        _parent = null;
                        IsEndElement = true;
                        return false;
                    }
                }
            }
            return ReadToEnd();
        }
 
        public override void ResolveEntity()
        {
        }
 
        public override void Skip()
        {
            if (!IsInteractive)
            {
                return;
            }
            Read(true);
        }
 
        bool IXmlLineInfo.HasLineInfo()
        {
            if (IsEndElement)
            {
                // Special case for EndElement - we store the line info differently in this case
                //   we also know that the current node (source) is XElement
                XElement? e = _source as XElement;
                if (e != null)
                {
                    return e.Annotation<LineInfoEndElementAnnotation>() != null;
                }
            }
            else
            {
                IXmlLineInfo? li = _source as IXmlLineInfo;
                if (li != null)
                {
                    return li.HasLineInfo();
                }
            }
            return false;
        }
 
        int IXmlLineInfo.LineNumber
        {
            get
            {
                if (IsEndElement)
                {
                    // Special case for EndElement - we store the line info differently in this case
                    //   we also know that the current node (source) is XElement
                    XElement? e = _source as XElement;
                    if (e != null)
                    {
                        LineInfoEndElementAnnotation? a = e.Annotation<LineInfoEndElementAnnotation>();
                        if (a != null)
                        {
                            return a.lineNumber;
                        }
                    }
                }
                else
                {
                    IXmlLineInfo? li = _source as IXmlLineInfo;
                    if (li != null)
                    {
                        return li.LineNumber;
                    }
                }
                return 0;
            }
        }
 
        int IXmlLineInfo.LinePosition
        {
            get
            {
                if (IsEndElement)
                {
                    // Special case for EndElement - we store the line info differently in this case
                    //   we also know that the current node (source) is XElement
                    XElement? e = _source as XElement;
                    if (e != null)
                    {
                        LineInfoEndElementAnnotation? a = e.Annotation<LineInfoEndElementAnnotation>();
                        if (a != null)
                        {
                            return a.linePosition;
                        }
                    }
                }
                else
                {
                    IXmlLineInfo? li = _source as IXmlLineInfo;
                    if (li != null)
                    {
                        return li.LinePosition;
                    }
                }
                return 0;
            }
        }
 
        private bool IsEndElement
        {
            get { return _parent == _source; }
            set { _parent = value ? _source : null; }
        }
 
        private bool IsInteractive
        {
            get { return _state == ReadState.Interactive; }
        }
 
        private static NameTable CreateNameTable()
        {
            var nameTable = new NameTable();
            nameTable.Add(string.Empty);
            nameTable.Add(XNamespace.xmlnsPrefixNamespace);
            nameTable.Add(XNamespace.xmlPrefixNamespace);
            return nameTable;
        }
 
        private XElement? GetElementInAttributeScope()
        {
            XElement? e = _source as XElement;
            if (e != null)
            {
                if (IsEndElement)
                {
                    return null;
                }
                return e;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                return (XElement?)a.parent;
            }
            a = _parent as XAttribute;
            if (a != null)
            {
                return (XElement?)a.parent;
            }
            return null;
        }
 
        private XElement? GetElementInScope()
        {
            XElement? e = _source as XElement;
            if (e != null)
            {
                return e;
            }
            XNode? n = _source as XNode;
            if (n != null)
            {
                return n.parent as XElement;
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                return (XElement?)a.parent;
            }
            e = _parent as XElement;
            if (e != null)
            {
                return e;
            }
            a = _parent as XAttribute;
            if (a != null)
            {
                return (XElement?)a.parent;
            }
            return null;
        }
 
        private static void GetNameInAttributeScope(string? qualifiedName, XElement e, out string? localName, out string? namespaceName)
        {
            if (!string.IsNullOrEmpty(qualifiedName))
            {
                int i = qualifiedName.IndexOf(':');
                if (i != 0 && i != qualifiedName.Length - 1)
                {
                    if (i == -1)
                    {
                        localName = qualifiedName;
                        namespaceName = string.Empty;
                        return;
                    }
                    XNamespace? ns = e.GetNamespaceOfPrefix(qualifiedName.Substring(0, i));
                    if (ns != null)
                    {
                        localName = qualifiedName.Substring(i + 1);
                        namespaceName = ns.NamespaceName;
                        return;
                    }
                }
            }
            localName = null;
            namespaceName = null;
        }
 
        private bool Read(bool skipContent)
        {
            XElement? e = _source as XElement;
            if (e != null)
            {
                if (e.IsEmpty || IsEndElement || skipContent)
                {
                    return ReadOverNode(e);
                }
                return ReadIntoElement(e);
            }
            XNode? n = _source as XNode;
            if (n != null)
            {
                return ReadOverNode(n);
            }
            XAttribute? a = _source as XAttribute;
            if (a != null)
            {
                return ReadOverAttribute(a, skipContent);
            }
            return ReadOverText(skipContent);
        }
 
        private bool ReadIntoDocument(XDocument d)
        {
            XNode? n = d.content as XNode;
            if (n != null)
            {
                _source = n.next!;
                return true;
            }
            string? s = d.content as string;
            if (s != null)
            {
                if (s.Length > 0)
                {
                    _source = s;
                    _parent = d;
                    return true;
                }
            }
            return ReadToEnd();
        }
 
        private bool ReadIntoElement(XElement e)
        {
            XNode? n = e.content as XNode;
            if (n != null)
            {
                _source = n.next!;
                return true;
            }
            string? s = e.content as string;
            if (s != null)
            {
                if (s.Length > 0)
                {
                    _source = s;
                    _parent = e;
                }
                else
                {
                    _source = e;
                    IsEndElement = true;
                }
                return true;
            }
            return ReadToEnd();
        }
 
        private bool ReadIntoAttribute(XAttribute a)
        {
            _source = a.value;
            _parent = a;
            return true;
        }
 
        private bool ReadOverAttribute(XAttribute a, bool skipContent)
        {
            XElement? e = (XElement?)a.parent;
            if (e != null)
            {
                if (e.IsEmpty || skipContent)
                {
                    return ReadOverNode(e);
                }
                return ReadIntoElement(e);
            }
            return ReadToEnd();
        }
 
        private bool ReadOverNode(XNode n)
        {
            if (n == _root)
            {
                return ReadToEnd();
            }
            XNode? next = n.next;
            if (null == next || next == n || n == n.parent!.content)
            {
                if (n.parent == null || (n.parent.parent == null && n.parent is XDocument))
                {
                    return ReadToEnd();
                }
                _source = n.parent;
                IsEndElement = true;
            }
            else
            {
                _source = next;
                IsEndElement = false;
            }
            return true;
        }
 
        private bool ReadOverText(bool skipContent)
        {
            if (_parent is XElement)
            {
                _source = _parent;
                _parent = null;
                IsEndElement = true;
                return true;
            }
 
            XAttribute? parent = _parent as XAttribute;
            if (parent != null)
            {
                _parent = null;
                return ReadOverAttribute(parent, skipContent);
            }
            return ReadToEnd();
        }
 
        private bool ReadToEnd()
        {
            _state = ReadState.EndOfFile;
            return false;
        }
 
        /// <summary>
        /// Determines if the specified attribute would be a duplicate namespace declaration
        ///  - one which we already reported on some ancestor, so it's not necessary to report it here
        /// </summary>
        /// <param name="candidateAttribute">The attribute to test.</param>
        /// <returns>true if the attribute is a duplicate namespace declaration attribute</returns>
        private bool IsDuplicateNamespaceAttribute(XAttribute candidateAttribute)
        {
            if (!candidateAttribute.IsNamespaceDeclaration)
            {
                return false;
            }
            else
            {
                // Split the method in two to enable inlining of this piece (Which will work for 95% of cases)
                return IsDuplicateNamespaceAttributeInner(candidateAttribute);
            }
        }
 
        private bool IsDuplicateNamespaceAttributeInner(XAttribute candidateAttribute)
        {
            // First of all - if this is an xmlns:xml declaration then it's a duplicate
            //   since xml prefix can't be redeclared and it's declared by default always.
            if (candidateAttribute.Name.LocalName == "xml")
            {
                return true;
            }
            // The algorithm we use is:
            //    Go up the tree (but don't go higher than the root of this reader)
            //    and find the closest namespace declaration attribute which declares the same prefix
            //    If it declares that prefix to the exact same URI as ours does then ours is a duplicate
            //    Note that if we find a namespace declaration for the same prefix but with a different URI, then we don't have a dupe!
            XElement? element = candidateAttribute.parent as XElement;
            if (element == _root || element == null)
            {
                // If there's only the parent element of our attribute, there can be no duplicates
                return false;
            }
            element = element.parent as XElement;
            while (element != null)
            {
                // Search all attributes of this element for the same prefix declaration
                // Trick - a declaration for the same prefix will have the exact same XName - so we can do a quick ref comparison of names
                // (The default ns decl is represented by an XName "xmlns{}", even if you try to create
                //  an attribute with XName "xmlns{http://www.w3.org/2000/xmlns/}" it will fail,
                //  because it's treated as a declaration of prefix "xmlns" which is invalid)
                XAttribute? a = element.lastAttr;
                if (a != null)
                {
                    do
                    {
                        if (a.name == candidateAttribute.name)
                        {
                            // Found the same prefix decl
                            if (a.Value == candidateAttribute.Value)
                            {
                                // And it's for the same namespace URI as well - so ours is a duplicate
                                return true;
                            }
                            else
                            {
                                // It's not for the same namespace URI - which means we have to keep ours
                                //   (no need to continue the search as this one overrides anything above it)
                                return false;
                            }
                        }
                        a = a.next!;
                    } while (a != element.lastAttr);
                }
                if (element == _root)
                {
                    return false;
                }
                element = element.parent as XElement;
            }
            return false;
        }
 
        /// <summary>
        /// Finds a first attribute (starting with the parameter) which is not a duplicate namespace attribute
        /// </summary>
        /// <param name="candidate">The attribute to start with</param>
        /// <returns>The first attribute which is not a namespace attribute or null if the end of attributes has bean reached</returns>
        private XAttribute? GetFirstNonDuplicateNamespaceAttribute(XAttribute candidate)
        {
            Debug.Assert(_omitDuplicateNamespaces, "This method should only be called if we're omitting duplicate namespace attribute. For perf reason it's better to test this flag in the caller method.");
            if (!IsDuplicateNamespaceAttribute(candidate))
            {
                return candidate;
            }
 
            XElement? e = candidate.parent as XElement;
            if (e != null && candidate != e.lastAttr)
            {
                do
                {
                    candidate = candidate.next!;
                    if (!IsDuplicateNamespaceAttribute(candidate))
                    {
                        return candidate;
                    }
                } while (candidate != e.lastAttr);
            }
            return null;
        }
    }
}