File: System\Xml\Core\XsdValidatingReader.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;
using System.Text;
using System.Xml.Schema;
using System.Xml.XPath;
 
namespace System.Xml
{
    internal delegate void CachingEventHandler(XsdCachingReader cachingReader);
 
    internal sealed class AttributePSVIInfo
    {
        internal string? localName;
        internal string? namespaceUri;
        internal object? typedAttributeValue;
        internal XmlSchemaInfo attributeSchemaInfo;
 
        internal AttributePSVIInfo()
        {
            attributeSchemaInfo = new XmlSchemaInfo();
        }
 
        internal void Reset()
        {
            typedAttributeValue = null;
            localName = string.Empty;
            namespaceUri = string.Empty;
            attributeSchemaInfo.Clear();
        }
    }
 
    internal sealed partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver
    {
        private enum ValidatingReaderState
        {
            None = 0,
            Init = 1,
            Read = 2,
            OnDefaultAttribute = -1,
            OnReadAttributeValue = -2,
            OnAttribute = 3,
            ClearAttributes = 4,
            ParseInlineSchema = 5,
            ReadAhead = 6,
            OnReadBinaryContent = 7,
            ReaderClosed = 8,
            EOF = 9,
            Error = 10,
        }
 
        // Validation
        private XmlReader _coreReader;
        private readonly IXmlNamespaceResolver? _coreReaderNSResolver;
        private readonly IXmlNamespaceResolver _thisNSResolver;
        private XmlSchemaValidator _validator = null!;
        private readonly XmlResolver? _xmlResolver;
        private readonly ValidationEventHandler? _validationEvent;
        private ValidatingReaderState _validationState;
        private readonly XmlValueGetter _valueGetter;
 
        // namespace management
        private readonly XmlNamespaceManager? _nsManager;
        private readonly bool _manageNamespaces;
        private readonly bool _processInlineSchema;
        private bool _replayCache;
 
        // Current Node handling
        private ValidatingReaderNodeData? _cachedNode; // Used to cache current node when looking ahead or default attributes
        private AttributePSVIInfo? _attributePSVI;
 
        // Attributes
        private int _attributeCount; // Total count of attributes including default
        private int _coreReaderAttributeCount;
        private int _currentAttrIndex;
        private AttributePSVIInfo[] _attributePSVINodes;
        private readonly ArrayList _defaultAttributes;
 
        // Inline Schema
        private Parser? _inlineSchemaParser;
 
        // Typed Value & PSVI
        private object? _atomicValue;
        private readonly XmlSchemaInfo _xmlSchemaInfo;
 
        // original string of the atomic value
        private string? _originalAtomicValueString;
 
        // cached coreReader information
        private readonly XmlNameTable _coreReaderNameTable;
        private XsdCachingReader? _cachingReader;
 
        // ReadAttributeValue TextNode
        private ValidatingReaderNodeData? _textNode;
 
        // To avoid SchemaNames creation
        private readonly string _nsXmlNs;
        private readonly string _nsXs;
        private readonly string _nsXsi;
        private readonly string _xsiType;
        private readonly string _xsiNil;
        private readonly string _xsdSchema;
        private readonly string _xsiSchemaLocation;
        private readonly string _xsiNoNamespaceSchemaLocation;
 
        // Underlying reader's IXmlLineInfo
        private IXmlLineInfo? _lineInfo;
 
        // helpers for Read[Element]ContentAs{Base64,BinHex} methods
        private ReadContentAsBinaryHelper? _readBinaryHelper;
        private ValidatingReaderState _savedState;
 
        // Constants
        private const int InitialAttributeCount = 8;
 
        private static volatile Type s_typeOfString = null!;
 
        // Constructor
        internal XsdValidatingReader(XmlReader reader, XmlResolver? xmlResolver, XmlReaderSettings readerSettings, XmlSchemaObject? partialValidationType)
        {
            _coreReader = reader;
            _coreReaderNSResolver = reader as IXmlNamespaceResolver;
            _lineInfo = reader as IXmlLineInfo;
            _coreReaderNameTable = _coreReader.NameTable;
            if (_coreReaderNSResolver == null)
            {
                _nsManager = new XmlNamespaceManager(_coreReaderNameTable);
                _manageNamespaces = true;
            }
 
            _thisNSResolver = this;
            _xmlResolver = xmlResolver;
            _processInlineSchema = (readerSettings.ValidationFlags & XmlSchemaValidationFlags.ProcessInlineSchema) != 0;
 
            _validationState = ValidatingReaderState.Init;
            _defaultAttributes = new ArrayList();
            _currentAttrIndex = -1;
            _attributePSVINodes = new AttributePSVIInfo[InitialAttributeCount];
            _valueGetter = new XmlValueGetter(GetStringValue);
            s_typeOfString = typeof(string);
            _xmlSchemaInfo = new XmlSchemaInfo();
 
            // Add common strings to be compared to NameTable
            _nsXmlNs = _coreReaderNameTable.Add(XmlReservedNs.NsXmlNs);
            _nsXs = _coreReaderNameTable.Add(XmlReservedNs.NsXs);
            _nsXsi = _coreReaderNameTable.Add(XmlReservedNs.NsXsi);
            _xsiType = _coreReaderNameTable.Add("type");
            _xsiNil = _coreReaderNameTable.Add("nil");
            _xsiSchemaLocation = _coreReaderNameTable.Add("schemaLocation");
            _xsiNoNamespaceSchemaLocation = _coreReaderNameTable.Add("noNamespaceSchemaLocation");
            _xsdSchema = _coreReaderNameTable.Add("schema");
 
            SetupValidator(readerSettings, reader, partialValidationType);
            _validationEvent = readerSettings.GetEventHandler();
        }
 
        internal XsdValidatingReader(XmlReader reader, XmlResolver? xmlResolver, XmlReaderSettings readerSettings)
            : this(reader, xmlResolver, readerSettings, null)
        { }
 
        [MemberNotNull(nameof(_validator))]
        private void SetupValidator(XmlReaderSettings readerSettings, XmlReader reader, XmlSchemaObject? partialValidationType)
        {
            _validator = new XmlSchemaValidator(_coreReaderNameTable, readerSettings.Schemas, _thisNSResolver, readerSettings.ValidationFlags);
            _validator.XmlResolver = _xmlResolver;
            _validator.SourceUri = XmlConvert.ToUri(reader.BaseURI); // Not using XmlResolver.ResolveUri as it checks for relative Uris,reader.BaseURI will be absolute file paths or string.Empty
            _validator.ValidationEventSender = this;
            _validator.ValidationEventHandler += readerSettings.GetEventHandler();
            _validator.LineInfoProvider = _lineInfo;
 
            if (_validator.ProcessSchemaHints)
            {
                _validator.SchemaSet.ReaderSettings.DtdProcessing = readerSettings.DtdProcessing;
            }
 
            _validator.SetDtdSchemaInfo(reader.DtdInfo);
            if (partialValidationType != null)
            {
                _validator.Initialize(partialValidationType);
            }
            else
            {
                _validator.Initialize();
            }
        }
 
        // Settings
        public override XmlReaderSettings Settings
        {
            get
            {
                XmlReaderSettings? settings = _coreReader.Settings;
                settings = settings != null ? settings.Clone() : new XmlReaderSettings();
 
                settings.Schemas = _validator.SchemaSet;
                settings.ValidationType = ValidationType.Schema;
                settings.ValidationFlags = _validator.ValidationFlags;
                settings.ReadOnly = true;
                return settings;
            }
        }
 
        // Node Properties
 
        // Gets the type of the current node.
        public override XmlNodeType NodeType
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.NodeType;
                }
                else
                {
                    XmlNodeType nodeType = _coreReader.NodeType;
                    // Check for significant whitespace
                    if (nodeType == XmlNodeType.Whitespace && (_validator.CurrentContentType == XmlSchemaContentType.TextOnly || _validator.CurrentContentType == XmlSchemaContentType.Mixed))
                    {
                        return XmlNodeType.SignificantWhitespace;
                    }
 
                    return nodeType;
                }
            }
        }
 
        // Gets the name of the current node, including the namespace prefix.
        public override string Name
        {
            get
            {
                if (_validationState == ValidatingReaderState.OnDefaultAttribute)
                {
                    Debug.Assert(_cachedNode != null);
                    string? prefix = _validator.GetDefaultAttributePrefix(_cachedNode.Namespace);
                    if (!string.IsNullOrEmpty(prefix))
                    {
                        return $"{prefix}:{_cachedNode.LocalName}";
                    }
 
                    return _cachedNode.LocalName;
                }
 
                return _coreReader.Name;
            }
        }
 
        // Gets the name of the current node without the namespace prefix.
        public override string LocalName
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.LocalName;
                }
 
                return _coreReader.LocalName;
            }
        }
 
        // Gets the namespace URN (as defined in the W3C Namespace Specification) of the current namespace scope.
        public override string NamespaceURI
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.Namespace;
                }
 
                return _coreReader.NamespaceURI;
            }
        }
 
        // Gets the namespace prefix associated with the current node.
        public override string Prefix
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.Prefix;
                }
 
                return _coreReader.Prefix;
            }
        }
 
        // Gets a value indicating whether the current node can have a non-empty Value
        public override bool HasValue
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    return true;
                }
 
                return _coreReader.HasValue;
            }
        }
 
        // Gets the text value of the current node.
        public override string Value
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.RawValue;
                }
 
                return _coreReader.Value;
            }
        }
 
        // Gets the depth of the current node in the XML element stack.
        public override int Depth
        {
            get
            {
                if ((int)_validationState < 0)
                {
                    Debug.Assert(_cachedNode != null);
                    return _cachedNode.Depth;
                }
 
                return _coreReader.Depth;
            }
        }
 
        // Gets the base URI of the current node.
        public override string BaseURI
        {
            get
            {
                return _coreReader.BaseURI;
            }
        }
 
        // Gets a value indicating whether the current node is an empty element (for example, <MyElement/>).
        public override bool IsEmptyElement
        {
            get
            {
                return _coreReader.IsEmptyElement;
            }
        }
 
        // Gets a value indicating whether the current node is an attribute that was generated from the default value defined
        // in the DTD or schema.
        public override bool IsDefault
        {
            get
            {
                if (_validationState == ValidatingReaderState.OnDefaultAttribute)
                {
                    // XSD default attributes
                    return true;
                }
 
                return _coreReader.IsDefault; // This is DTD Default attribute
            }
        }
 
        // Gets the quotation mark character used to enclose the value of an attribute node.
        public override char QuoteChar
        {
            get
            {
                return _coreReader.QuoteChar;
            }
        }
 
        // Gets the current xml:space scope.
        public override XmlSpace XmlSpace
        {
            get
            {
                return _coreReader.XmlSpace;
            }
        }
 
        // Gets the current xml:lang scope.
        public override string XmlLang
        {
            get
            {
                return _coreReader.XmlLang;
            }
        }
 
        public override IXmlSchemaInfo SchemaInfo
        {
            get
            {
                return this as IXmlSchemaInfo;
            }
        }
 
        public override System.Type ValueType
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                    case XmlNodeType.EndElement:
                        if (_xmlSchemaInfo.ContentType == XmlSchemaContentType.TextOnly)
                        {
                            Debug.Assert(_xmlSchemaInfo.SchemaType!.Datatype != null);
                            return _xmlSchemaInfo.SchemaType.Datatype.ValueType;
                        }
 
                        goto default;
 
                    case XmlNodeType.Attribute:
                        if (_attributePSVI != null && AttributeSchemaInfo.ContentType == XmlSchemaContentType.TextOnly)
                        {
                            Debug.Assert(AttributeSchemaInfo.SchemaType!.Datatype != null);
                            return AttributeSchemaInfo.SchemaType.Datatype.ValueType;
                        }
 
                        goto default;
 
                    default:
                        return s_typeOfString;
                }
            }
        }
 
        public override object ReadContentAsObject()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsObject));
            }
 
            return InternalReadContentAsObject(true);
        }
 
        public override bool ReadContentAsBoolean()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsBoolean));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToBoolean(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToBoolean(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
        }
 
        public override DateTime ReadContentAsDateTime()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsDateTime));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDateTime(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDateTime(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
        }
 
        public override double ReadContentAsDouble()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsDouble));
            }
 
            object? typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDouble(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDouble(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
        }
 
        public override float ReadContentAsFloat()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsFloat));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToSingle(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToSingle(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
        }
 
        public override decimal ReadContentAsDecimal()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsDecimal));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDecimal(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDecimal(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
        }
 
        public override int ReadContentAsInt()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsInt));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToInt32(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToInt32(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
        }
 
        public override long ReadContentAsLong()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsLong));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToInt64(typedValue);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToInt64(typedValue);
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
        }
 
        public override string ReadContentAsString()
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAsString));
            }
 
            object typedValue = InternalReadContentAsObject();
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToString(typedValue);
                }
                else
                {
                    return (typedValue as string)!;
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
        }
 
        public override object ReadContentAs(Type returnType, IXmlNamespaceResolver? namespaceResolver)
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(nameof(ReadContentAs));
            }
 
            string originalStringValue;
            object typedValue = InternalReadContentAsObject(false, out originalStringValue);
            XmlSchemaType? xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType;
            try
            {
                if (xmlType != null)
                {
                    // special-case conversions to DateTimeOffset; typedValue is by default a DateTime
                    // which cannot preserve time zone, so we need to convert from the original string
                    if (returnType == typeof(DateTimeOffset) && xmlType.Datatype is Datatype_dateTimeBase)
                    {
                        typedValue = originalStringValue!;
                    }
 
                    return xmlType.ValueConverter.ChangeType(typedValue, returnType);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ChangeType(typedValue, returnType, namespaceResolver);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
        }
 
        public override object ReadElementContentAsObject()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsObject));
            }
 
            XmlSchemaType? xmlType;
            return InternalReadElementContentAsObject(out xmlType, true)!;
        }
 
        public override bool ReadElementContentAsBoolean()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsBoolean));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToBoolean(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToBoolean(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, this as IXmlLineInfo);
            }
        }
 
        public override DateTime ReadElementContentAsDateTime()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsDateTime));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDateTime(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDateTime(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, this as IXmlLineInfo);
            }
        }
 
        public override double ReadElementContentAsDouble()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsDouble));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDouble(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDouble(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, this as IXmlLineInfo);
            }
        }
 
        public override float ReadElementContentAsFloat()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsFloat));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToSingle(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToSingle(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, this as IXmlLineInfo);
            }
        }
 
        public override decimal ReadElementContentAsDecimal()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsDecimal));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToDecimal(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToDecimal(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, this as IXmlLineInfo);
            }
        }
 
        public override int ReadElementContentAsInt()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsInt));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToInt32(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToInt32(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int", e, this as IXmlLineInfo);
            }
        }
 
        public override long ReadElementContentAsLong()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsLong));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null)
                {
                    return xmlType.ValueConverter.ToInt64(typedValue!);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ToInt64(typedValue!);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "Long", e, this as IXmlLineInfo);
            }
        }
 
        public override string ReadElementContentAsString()
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAsString));
            }
 
            XmlSchemaType? xmlType;
            object? typedValue = InternalReadElementContentAsObject(out xmlType);
 
            try
            {
                if (xmlType != null && typedValue != null)
                {
                    return xmlType.ValueConverter.ToString(typedValue);
                }
                else
                {
                    return typedValue as string ?? string.Empty;
                }
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
        }
 
        public override object ReadElementContentAs(Type returnType, IXmlNamespaceResolver namespaceResolver)
        {
            if (this.NodeType != XmlNodeType.Element)
            {
                throw CreateReadElementContentAsException(nameof(ReadElementContentAs));
            }
 
            XmlSchemaType? xmlType;
            string? originalStringValue;
            object? typedValue = InternalReadElementContentAsObject(out xmlType, false, out originalStringValue);
 
            try
            {
                if (xmlType != null)
                {
                    // special-case conversions to DateTimeOffset; typedValue is by default a DateTime
                    // which cannot preserve time zone, so we need to convert from the original string
                    if (returnType == typeof(DateTimeOffset) && xmlType.Datatype is Datatype_dateTimeBase)
                    {
                        typedValue = originalStringValue;
                    }
 
                    return xmlType.ValueConverter.ChangeType(typedValue!, returnType, namespaceResolver);
                }
                else
                {
                    return XmlUntypedConverter.Untyped.ChangeType(typedValue!, returnType, namespaceResolver);
                }
            }
            catch (FormatException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (OverflowException e)
            {
                throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
        }
 
        // Attribute Accessors
 
        // The number of attributes on the current node.
        public override int AttributeCount
        {
            get
            {
                return _attributeCount;
            }
        }
 
        // Gets the value of the attribute with the specified Name.
        public override string? GetAttribute(string name)
        {
            string? attValue = _coreReader.GetAttribute(name);
 
            if (attValue == null && _attributeCount > 0)
            {
                // Could be default attribute
                ValidatingReaderNodeData? defaultNode = GetDefaultAttribute(name, false);
                if (defaultNode != null)
                {
                    // Default found
                    attValue = defaultNode.RawValue;
                }
            }
 
            return attValue;
        }
 
        // Gets the value of the attribute with the specified LocalName and NamespaceURI.
        public override string? GetAttribute(string name, string? namespaceURI)
        {
            string? attValue = _coreReader.GetAttribute(name, namespaceURI);
 
            if (attValue == null && _attributeCount > 0)
            {
                // Could be default attribute
                string? atomizedNamespaceURI = (namespaceURI == null) ? string.Empty : _coreReaderNameTable.Get(namespaceURI);
                string? atomizedName = _coreReaderNameTable.Get(name);
 
                if (atomizedName == null || atomizedNamespaceURI == null)
                {
                    // Attribute not present since we did not see it
                    return null;
                }
 
                ValidatingReaderNodeData? attNode = GetDefaultAttribute(atomizedName, atomizedNamespaceURI, false);
                if (attNode != null)
                {
                    return attNode.RawValue;
                }
            }
 
            return attValue;
        }
 
        // Gets the value of the attribute with the specified index.
        public override string GetAttribute(int i)
        {
            ArgumentOutOfRangeException.ThrowIfNegative(i);
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(i, _attributeCount);
 
            if (i < _coreReaderAttributeCount)
            {
                return _coreReader.GetAttribute(i);
            }
            else
            {
                int defaultIndex = i - _coreReaderAttributeCount;
                ValidatingReaderNodeData attNode = (ValidatingReaderNodeData)_defaultAttributes[defaultIndex]!;
                Debug.Assert(attNode != null);
                return attNode.RawValue;
            }
        }
 
        // Moves to the attribute with the specified Name
        public override bool MoveToAttribute(string name)
        {
            if (_coreReader.MoveToAttribute(name))
            {
                _validationState = ValidatingReaderState.OnAttribute;
                _attributePSVI = GetAttributePSVI(name);
                goto Found;
            }
            else if (_attributeCount > 0)
            {
                // Default attribute
                ValidatingReaderNodeData? defaultNode = GetDefaultAttribute(name, true);
                if (defaultNode != null)
                {
                    _validationState = ValidatingReaderState.OnDefaultAttribute;
                    _attributePSVI = defaultNode.AttInfo;
                    _cachedNode = defaultNode;
                    goto Found;
                }
            }
 
            return false;
        Found:
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
 
            return true;
        }
 
        // Moves to the attribute with the specified LocalName and NamespaceURI
        public override bool MoveToAttribute(string name, string? ns)
        {
            // Check atomized local name and ns
            string? atomizedName = _coreReaderNameTable.Get(name);
            ns = ns != null ? _coreReaderNameTable.Get(ns) : string.Empty;
 
            if (atomizedName == null || ns == null)
            {
                // Name or ns not found in the nameTable, then attribute is not found
                return false;
            }
 
            if (_coreReader.MoveToAttribute(atomizedName, ns))
            {
                _validationState = ValidatingReaderState.OnAttribute;
                if (_inlineSchemaParser == null)
                {
                    _attributePSVI = GetAttributePSVI(atomizedName, ns);
                    Debug.Assert(_attributePSVI != null);
                }
                else
                {
                    // Parsing inline schema, no PSVI for schema attributes
                    _attributePSVI = null;
                }
 
                goto Found;
            }
            else
            {
                // Default attribute
                ValidatingReaderNodeData? defaultNode = GetDefaultAttribute(atomizedName, ns, true);
                if (defaultNode != null)
                {
                    _attributePSVI = defaultNode.AttInfo;
                    _cachedNode = defaultNode;
                    _validationState = ValidatingReaderState.OnDefaultAttribute;
                    goto Found;
                }
            }
 
            return false;
 
        Found:
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
 
            return true;
        }
 
        // Moves to the attribute with the specified index
        public override void MoveToAttribute(int i)
        {
            ArgumentOutOfRangeException.ThrowIfNegative(i);
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(i, _attributeCount);
 
            _currentAttrIndex = i;
            if (i < _coreReaderAttributeCount)
            {
                // reader attribute
                _coreReader.MoveToAttribute(i);
                if (_inlineSchemaParser == null)
                {
                    _attributePSVI = _attributePSVINodes[i];
                }
                else
                {
                    _attributePSVI = null;
                }
 
                _validationState = ValidatingReaderState.OnAttribute;
            }
            else
            {
                // default attribute
                int defaultIndex = i - _coreReaderAttributeCount;
                _cachedNode = (ValidatingReaderNodeData)_defaultAttributes[defaultIndex]!;
                _attributePSVI = _cachedNode.AttInfo;
                _validationState = ValidatingReaderState.OnDefaultAttribute;
            }
 
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
        }
 
        // Moves to the first attribute.
        public override bool MoveToFirstAttribute()
        {
            if (_coreReader.MoveToFirstAttribute())
            {
                _currentAttrIndex = 0;
                if (_inlineSchemaParser == null)
                {
                    _attributePSVI = _attributePSVINodes[0];
                }
                else
                {
                    _attributePSVI = null;
                }
 
                _validationState = ValidatingReaderState.OnAttribute;
                goto Found;
            }
            else if (_defaultAttributes.Count > 0)
            {
                // check for default
                _cachedNode = (ValidatingReaderNodeData)_defaultAttributes[0]!;
                _attributePSVI = _cachedNode.AttInfo;
                _currentAttrIndex = 0;
                _validationState = ValidatingReaderState.OnDefaultAttribute;
                goto Found;
            }
 
            return false;
        Found:
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
 
            return true;
        }
 
        // Moves to the next attribute.
        public override bool MoveToNextAttribute()
        {
            if (_currentAttrIndex + 1 < _coreReaderAttributeCount)
            {
                bool moveTo = _coreReader.MoveToNextAttribute();
                Debug.Assert(moveTo);
                _currentAttrIndex++;
                if (_inlineSchemaParser == null)
                {
                    _attributePSVI = _attributePSVINodes[_currentAttrIndex];
                }
                else
                {
                    _attributePSVI = null;
                }
 
                _validationState = ValidatingReaderState.OnAttribute;
                goto Found;
            }
            else if (_currentAttrIndex + 1 < _attributeCount)
            {
                // default attribute
                int defaultIndex = ++_currentAttrIndex - _coreReaderAttributeCount;
                _cachedNode = (ValidatingReaderNodeData)_defaultAttributes[defaultIndex]!;
                _attributePSVI = _cachedNode.AttInfo;
                _validationState = ValidatingReaderState.OnDefaultAttribute;
                goto Found;
            }
 
            return false;
        Found:
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
 
            return true;
        }
 
        // Moves to the element that contains the current attribute node.
        public override bool MoveToElement()
        {
            if (_coreReader.MoveToElement() || (int)_validationState < 0)
            {
                // states OnDefaultAttribute or OnReadAttributeValue
                _currentAttrIndex = -1;
                _validationState = ValidatingReaderState.ClearAttributes;
                return true;
            }
 
            return false;
        }
 
        // Reads the next node from the stream/TextReader.
        public override bool Read()
        {
            switch (_validationState)
            {
                case ValidatingReaderState.Read:
                    if (_coreReader.Read())
                    {
                        ProcessReaderEvent();
                        return true;
                    }
                    else
                    {
                        _validator.EndValidation();
                        if (_coreReader.EOF)
                        {
                            _validationState = ValidatingReaderState.EOF;
                        }
 
                        return false;
                    }
 
                case ValidatingReaderState.ParseInlineSchema:
                    ProcessInlineSchema();
                    return true;
 
                case ValidatingReaderState.OnAttribute:
                case ValidatingReaderState.OnDefaultAttribute:
                case ValidatingReaderState.ClearAttributes:
                case ValidatingReaderState.OnReadAttributeValue:
                    ClearAttributesInfo();
                    if (_inlineSchemaParser != null)
                    {
                        _validationState = ValidatingReaderState.ParseInlineSchema;
                        goto case ValidatingReaderState.ParseInlineSchema;
                    }
                    else
                    {
                        _validationState = ValidatingReaderState.Read;
                        goto case ValidatingReaderState.Read;
                    }
 
                case ValidatingReaderState.ReadAhead: // Will enter here on calling Skip()
                    ClearAttributesInfo();
                    ProcessReaderEvent();
                    _validationState = ValidatingReaderState.Read;
                    return true;
 
                case ValidatingReaderState.OnReadBinaryContent:
                    _validationState = _savedState;
                    Debug.Assert(_readBinaryHelper != null);
                    _readBinaryHelper.Finish();
                    return Read();
 
                case ValidatingReaderState.Init:
                    _validationState = ValidatingReaderState.Read;
                    if (_coreReader.ReadState == ReadState.Interactive)
                    {
                        // If the underlying reader is already positioned on a ndoe, process it
                        ProcessReaderEvent();
                        return true;
                    }
                    else
                    {
                        goto case ValidatingReaderState.Read;
                    }
 
                case ValidatingReaderState.ReaderClosed:
                case ValidatingReaderState.EOF:
                    return false;
 
                default:
                    return false;
            }
        }
 
        // Gets a value indicating whether XmlReader is positioned at the end of the stream/TextReader.
        public override bool EOF
        {
            get
            {
                return _coreReader.EOF;
            }
        }
 
        // Closes the stream, changes the ReadState to Closed, and sets all the properties back to zero.
        public override void Close()
        {
            _coreReader.Close();
            _validationState = ValidatingReaderState.ReaderClosed;
        }
 
        // Returns the read state of the XmlReader.
        public override ReadState ReadState
        {
            get
            {
                return (_validationState == ValidatingReaderState.Init) ? ReadState.Initial : _coreReader.ReadState;
            }
        }
 
        // Skips to the end tag of the current element.
        public override void Skip()
        {
            switch (NodeType)
            {
                case XmlNodeType.Element:
                    if (_coreReader.IsEmptyElement)
                    {
                        break;
                    }
 
                    bool callSkipToEndElem = true;
                    // If union and unionValue has been parsed till EndElement, then validator.ValidateEndElement has been called
                    // Hence should not call SkipToEndElement as the current context has already been popped in the validator
                    if ((_xmlSchemaInfo.IsUnionType || _xmlSchemaInfo.IsDefault) && _coreReader is XsdCachingReader)
                    {
                        callSkipToEndElem = false;
                    }
 
                    _coreReader.Skip();
                    _validationState = ValidatingReaderState.ReadAhead;
                    if (callSkipToEndElem)
                    {
                        _validator.SkipToEndElement(_xmlSchemaInfo);
                    }
 
                    break;
 
                case XmlNodeType.Attribute:
                    MoveToElement();
                    goto case XmlNodeType.Element;
            }
            // For all other NodeTypes Skip() same as Read()
            Read();
            return;
        }
 
        // Gets the XmlNameTable associated with this implementation.
        public override XmlNameTable NameTable
        {
            get
            {
                return _coreReaderNameTable;
            }
        }
 
        // Resolves a namespace prefix in the current element's scope.
        public override string? LookupNamespace(string prefix)
        {
            return _thisNSResolver.LookupNamespace(prefix);
        }
 
        // Resolves the entity reference for nodes of NodeType EntityReference.
        public override void ResolveEntity()
        {
            throw new InvalidOperationException();
        }
 
        // Parses the attribute value into one or more Text and/or EntityReference node types.
        public override bool ReadAttributeValue()
        {
            if (_validationState == ValidatingReaderState.OnReadBinaryContent)
            {
                Debug.Assert(_readBinaryHelper != null);
                _readBinaryHelper.Finish();
                _validationState = _savedState;
            }
 
            if (NodeType == XmlNodeType.Attribute)
            {
                if (_validationState == ValidatingReaderState.OnDefaultAttribute)
                {
                    Debug.Assert(_cachedNode != null);
                    _cachedNode = CreateDummyTextNode(_cachedNode.RawValue, _cachedNode.Depth + 1);
                    _validationState = ValidatingReaderState.OnReadAttributeValue;
                    return true;
                }
 
                return _coreReader.ReadAttributeValue();
            }
 
            return false;
        }
 
        public override bool CanReadBinaryContent
        {
            get
            {
                return true;
            }
        }
 
        public override int ReadContentAsBase64(byte[] buffer, int index, int count)
        {
            if (ReadState != ReadState.Interactive)
            {
                return 0;
            }
 
            // init ReadContentAsBinaryHelper when called first time
            if (_validationState != ValidatingReaderState.OnReadBinaryContent)
            {
                _readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset(_readBinaryHelper, this);
                _savedState = _validationState;
            }
 
            // restore original state in order to have a normal Read() behavior when called from readBinaryHelper
            _validationState = _savedState;
 
            // call to the helper
            Debug.Assert(_readBinaryHelper != null);
            int readCount = _readBinaryHelper.ReadContentAsBase64(buffer, index, count);
 
            // set OnReadBinaryContent state again and return
            _savedState = _validationState;
            _validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override int ReadContentAsBinHex(byte[] buffer, int index, int count)
        {
            if (ReadState != ReadState.Interactive)
            {
                return 0;
            }
 
            // init ReadContentAsBinaryHelper when called first time
            if (_validationState != ValidatingReaderState.OnReadBinaryContent)
            {
                _readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset(_readBinaryHelper, this);
                _savedState = _validationState;
            }
 
            // restore original state in order to have a normal Read() behavior when called from readBinaryHelper
            _validationState = _savedState;
 
            // call to the helper
            Debug.Assert(_readBinaryHelper != null);
            int readCount = _readBinaryHelper.ReadContentAsBinHex(buffer, index, count);
 
            // set OnReadBinaryContent state again and return
            _savedState = _validationState;
            _validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override int ReadElementContentAsBase64(byte[] buffer, int index, int count)
        {
            if (ReadState != ReadState.Interactive)
            {
                return 0;
            }
 
            // init ReadContentAsBinaryHelper when called first time
            if (_validationState != ValidatingReaderState.OnReadBinaryContent)
            {
                _readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset(_readBinaryHelper, this);
                _savedState = _validationState;
            }
 
            // restore original state in order to have a normal Read() behavior when called from readBinaryHelper
            _validationState = _savedState;
 
            // call to the helper
            Debug.Assert(_readBinaryHelper != null);
            int readCount = _readBinaryHelper.ReadElementContentAsBase64(buffer, index, count);
 
            // set OnReadBinaryContent state again and return
            _savedState = _validationState;
            _validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override int ReadElementContentAsBinHex(byte[] buffer, int index, int count)
        {
            if (ReadState != ReadState.Interactive)
            {
                return 0;
            }
 
            // init ReadContentAsBinaryHelper when called first time
            if (_validationState != ValidatingReaderState.OnReadBinaryContent)
            {
                _readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset(_readBinaryHelper, this);
                _savedState = _validationState;
            }
 
            // restore original state in order to have a normal Read() behavior when called from readBinaryHelper
            _validationState = _savedState;
 
            // call to the helper
            Debug.Assert(_readBinaryHelper != null);
            int readCount = _readBinaryHelper.ReadElementContentAsBinHex(buffer, index, count);
 
            // set OnReadBinaryContent state again and return
            _savedState = _validationState;
            _validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        //
        // IXmlSchemaInfo interface
        //
        bool IXmlSchemaInfo.IsDefault
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                        if (!_coreReader.IsEmptyElement)
                        {
                            GetIsDefault();
                        }
 
                        return _xmlSchemaInfo.IsDefault;
 
                    case XmlNodeType.EndElement:
                        return _xmlSchemaInfo.IsDefault;
 
                    case XmlNodeType.Attribute:
                        if (_attributePSVI != null)
                        {
                            return AttributeSchemaInfo.IsDefault;
                        }
 
                        break;
 
                    default:
                        break;
                }
 
                return false;
            }
        }
 
        bool IXmlSchemaInfo.IsNil
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                    case XmlNodeType.EndElement:
                        return _xmlSchemaInfo.IsNil;
 
                    default:
                        break;
                }
 
                return false;
            }
        }
 
        XmlSchemaValidity IXmlSchemaInfo.Validity
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                        if (_coreReader.IsEmptyElement)
                        {
                            return _xmlSchemaInfo.Validity;
                        }
 
                        if (_xmlSchemaInfo.Validity == XmlSchemaValidity.Valid)
                        {
                            // It might be valid for unions since we read ahead, but report notknown for consistency
                            return XmlSchemaValidity.NotKnown;
                        }
 
                        return _xmlSchemaInfo.Validity;
 
                    case XmlNodeType.EndElement:
                        return _xmlSchemaInfo.Validity;
 
                    case XmlNodeType.Attribute:
                        if (_attributePSVI != null)
                        {
                            return AttributeSchemaInfo.Validity;
                        }
 
                        break;
                }
 
                return XmlSchemaValidity.NotKnown;
            }
        }
 
        XmlSchemaSimpleType? IXmlSchemaInfo.MemberType
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                        if (!_coreReader.IsEmptyElement)
                        {
                            GetMemberType();
                        }
 
                        return _xmlSchemaInfo.MemberType;
 
                    case XmlNodeType.EndElement:
                        return _xmlSchemaInfo.MemberType;
 
                    case XmlNodeType.Attribute:
                        if (_attributePSVI != null)
                        {
                            return AttributeSchemaInfo.MemberType;
                        }
 
                        return null;
 
                    default:
                        return null; // Text, PI, Comment etc
                }
            }
        }
 
        XmlSchemaType? IXmlSchemaInfo.SchemaType
        {
            get
            {
                switch (NodeType)
                {
                    case XmlNodeType.Element:
                    case XmlNodeType.EndElement:
                        return _xmlSchemaInfo.SchemaType;
 
                    case XmlNodeType.Attribute:
                        if (_attributePSVI != null)
                        {
                            return AttributeSchemaInfo.SchemaType;
                        }
 
                        return null;
 
                    default:
                        return null; // Text, PI, Comment etc
                }
            }
        }
 
        XmlSchemaElement? IXmlSchemaInfo.SchemaElement
        {
            get
            {
                if (NodeType == XmlNodeType.Element || NodeType == XmlNodeType.EndElement)
                {
                    return _xmlSchemaInfo.SchemaElement;
                }
 
                return null;
            }
        }
 
        XmlSchemaAttribute? IXmlSchemaInfo.SchemaAttribute
        {
            get
            {
                if (NodeType == XmlNodeType.Attribute)
                {
                    if (_attributePSVI != null)
                    {
                        return AttributeSchemaInfo.SchemaAttribute;
                    }
                }
 
                return null;
            }
        }
 
        //
        // IXmlLineInfo members
        //
 
        public bool HasLineInfo()
        {
            return true;
        }
 
        public int LineNumber
        {
            get
            {
                if (_lineInfo != null)
                {
                    return _lineInfo.LineNumber;
                }
 
                return 0;
            }
        }
 
        public int LinePosition
        {
            get
            {
                if (_lineInfo != null)
                {
                    return _lineInfo.LinePosition;
                }
 
                return 0;
            }
        }
 
        //
        // IXmlNamespaceResolver members
        //
        IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope)
        {
            if (_coreReaderNSResolver != null)
            {
                return _coreReaderNSResolver.GetNamespacesInScope(scope);
            }
            else
            {
                Debug.Assert(_nsManager != null);
                return _nsManager.GetNamespacesInScope(scope);
            }
        }
 
        string? IXmlNamespaceResolver.LookupNamespace(string prefix)
        {
            if (_coreReaderNSResolver != null)
            {
                return _coreReaderNSResolver.LookupNamespace(prefix);
            }
            else
            {
                Debug.Assert(_nsManager != null);
                return _nsManager.LookupNamespace(prefix);
            }
        }
 
        string? IXmlNamespaceResolver.LookupPrefix(string namespaceName)
        {
            if (_coreReaderNSResolver != null)
            {
                return _coreReaderNSResolver.LookupPrefix(namespaceName);
            }
            else
            {
                Debug.Assert(_nsManager != null);
                return _nsManager.LookupPrefix(namespaceName);
            }
        }
 
        // Internal / Private methods
 
        private string GetStringValue()
        {
            return _coreReader.Value;
        }
 
        private XmlSchemaType? ElementXmlType
        {
            get
            {
                return _xmlSchemaInfo.XmlType;
            }
        }
 
        private XmlSchemaType? AttributeXmlType
        {
            get
            {
                if (_attributePSVI != null)
                {
                    return AttributeSchemaInfo.XmlType;
                }
 
                return null;
            }
        }
 
        private XmlSchemaInfo AttributeSchemaInfo
        {
            get
            {
                Debug.Assert(_attributePSVI != null);
                return _attributePSVI.attributeSchemaInfo;
            }
        }
 
        private void ProcessReaderEvent()
        {
            if (_replayCache)
            {
                // if in replay mode, do nothing since nodes have been validated already
                // If NodeType == XmlNodeType.EndElement && if manageNamespaces, may need to pop namespace scope, since scope is not popped in ReadAheadForMemberType
 
                return;
            }
 
            switch (_coreReader.NodeType)
            {
                case XmlNodeType.Element:
 
                    ProcessElementEvent();
                    break;
 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    _validator.ValidateWhitespace(_valueGetter);
                    break;
 
                case XmlNodeType.Text:          // text inside a node
                case XmlNodeType.CDATA:         // <![CDATA[...]]>
                    _validator.ValidateText(_valueGetter);
                    break;
 
                case XmlNodeType.EndElement:
 
                    ProcessEndElementEvent();
                    break;
 
                case XmlNodeType.EntityReference:
                    throw new InvalidOperationException();
 
                case XmlNodeType.DocumentType:
#if TEMP_HACK_FOR_SCHEMA_INFO
                    validator.SetDtdSchemaInfo((SchemaInfo)coreReader.DtdInfo);
#else
                    _validator.SetDtdSchemaInfo(_coreReader.DtdInfo);
#endif
                    break;
 
                default:
                    break;
            }
        }
 
        private void ProcessElementEvent()
        {
            if (_processInlineSchema && IsXSDRoot(_coreReader.LocalName, _coreReader.NamespaceURI) && _coreReader.Depth > 0)
            {
                _xmlSchemaInfo.Clear();
                _attributeCount = _coreReaderAttributeCount = _coreReader.AttributeCount;
                if (!_coreReader.IsEmptyElement)
                {
                    // If its not empty schema, then parse else ignore
                    _inlineSchemaParser = new Parser(SchemaType.XSD, _coreReaderNameTable, _validator.SchemaSet.GetSchemaNames(_coreReaderNameTable), _validationEvent);
                    _inlineSchemaParser.StartParsing(_coreReader, null);
                    _inlineSchemaParser.ParseReaderNode();
                    _validationState = ValidatingReaderState.ParseInlineSchema;
                }
                else
                {
                    _validationState = ValidatingReaderState.ClearAttributes;
                }
            }
            else
            {
                // Validate element
                // Clear previous data
                _atomicValue = null;
                _originalAtomicValueString = null;
                _xmlSchemaInfo.Clear();
 
                if (_manageNamespaces)
                {
                    Debug.Assert(_nsManager != null);
                    _nsManager.PushScope();
                }
 
                // Find Xsi attributes that need to be processed before validating the element
                string? xsiSchemaLocation = null;
                string? xsiNoNamespaceSL = null;
                string? xsiNil = null;
                string? xsiType = null;
 
                if (_coreReader.MoveToFirstAttribute())
                {
                    do
                    {
                        string objectNs = _coreReader.NamespaceURI;
                        string objectName = _coreReader.LocalName;
 
                        if (Ref.Equal(objectNs, _nsXsi))
                        {
                            if (Ref.Equal(objectName, _xsiSchemaLocation))
                            {
                                xsiSchemaLocation = _coreReader.Value;
                            }
                            else if (Ref.Equal(objectName, _xsiNoNamespaceSchemaLocation))
                            {
                                xsiNoNamespaceSL = _coreReader.Value;
                            }
                            else if (Ref.Equal(objectName, _xsiType))
                            {
                                xsiType = _coreReader.Value;
                            }
                            else if (Ref.Equal(objectName, _xsiNil))
                            {
                                xsiNil = _coreReader.Value;
                            }
                        }
 
                        if (_manageNamespaces && Ref.Equal(_coreReader.NamespaceURI, _nsXmlNs))
                        {
                            Debug.Assert(_nsManager != null);
                            _nsManager.AddNamespace(_coreReader.Prefix.Length == 0 ? string.Empty : _coreReader.LocalName, _coreReader.Value);
                        }
                    } while (_coreReader.MoveToNextAttribute());
                    _coreReader.MoveToElement();
                }
 
                _validator.ValidateElement(_coreReader.LocalName, _coreReader.NamespaceURI, _xmlSchemaInfo, xsiType, xsiNil, xsiSchemaLocation, xsiNoNamespaceSL);
                ValidateAttributes();
                _validator.ValidateEndOfAttributes(_xmlSchemaInfo);
                if (_coreReader.IsEmptyElement)
                {
                    ProcessEndElementEvent();
                }
 
                _validationState = ValidatingReaderState.ClearAttributes;
            }
        }
 
        private void ProcessEndElementEvent()
        {
            _atomicValue = _validator.ValidateEndElement(_xmlSchemaInfo);
            _originalAtomicValueString = GetOriginalAtomicValueStringOfElement();
            if (_xmlSchemaInfo.IsDefault)
            {
                // The atomicValue returned is a default value
                Debug.Assert(_atomicValue != null);
                int depth = _coreReader.Depth;
                _coreReader = GetCachingReader();
                Debug.Assert(_cachingReader != null);
                _cachingReader.RecordTextNode(_xmlSchemaInfo.XmlType!.ValueConverter.ToString(_atomicValue), _originalAtomicValueString, depth + 1, 0, 0);
                _cachingReader.RecordEndElementNode();
                _cachingReader.SetToReplayMode();
                _replayCache = true;
            }
            else if (_manageNamespaces)
            {
                Debug.Assert(_nsManager != null);
                _nsManager.PopScope();
            }
        }
 
        private void ValidateAttributes()
        {
            _attributeCount = _coreReaderAttributeCount = _coreReader.AttributeCount;
            AttributePSVIInfo attributePSVI;
            int attIndex = 0;
            bool attributeInvalid = false;
            if (_coreReader.MoveToFirstAttribute())
            {
                do
                {
                    string localName = _coreReader.LocalName;
                    string ns = _coreReader.NamespaceURI;
 
                    attributePSVI = AddAttributePSVI(attIndex);
                    attributePSVI.localName = localName;
                    attributePSVI.namespaceUri = ns;
 
                    if ((object)ns == (object)_nsXmlNs)
                    {
                        attIndex++;
                        continue;
                    }
 
                    attributePSVI.typedAttributeValue = _validator.ValidateAttribute(localName, ns, _valueGetter, attributePSVI.attributeSchemaInfo);
                    if (!attributeInvalid)
                    {
                        attributeInvalid = attributePSVI.attributeSchemaInfo.Validity == XmlSchemaValidity.Invalid;
                    }
 
                    attIndex++;
                } while (_coreReader.MoveToNextAttribute());
            }
 
            _coreReader.MoveToElement();
            if (attributeInvalid)
            {
                // If any of the attributes are invalid, Need to report element's validity as invalid
                _xmlSchemaInfo.Validity = XmlSchemaValidity.Invalid;
            }
 
            _validator.GetUnspecifiedDefaultAttributes(_defaultAttributes, true);
            _attributeCount += _defaultAttributes.Count;
        }
 
        private void ClearAttributesInfo()
        {
            _attributeCount = 0;
            _coreReaderAttributeCount = 0;
            _currentAttrIndex = -1;
            _defaultAttributes.Clear();
            _attributePSVI = null;
        }
 
        private AttributePSVIInfo? GetAttributePSVI(string name)
        {
            if (_inlineSchemaParser != null)
            {
                // Parsing inline schema, no PSVI for schema attributes
                return null;
            }
 
            string attrLocalName;
            string attrPrefix;
            string ns;
            ValidateNames.SplitQName(name, out attrPrefix, out attrLocalName);
            attrPrefix = _coreReaderNameTable.Add(attrPrefix);
            attrLocalName = _coreReaderNameTable.Add(attrLocalName);
 
            if (attrPrefix.Length == 0)
            {
                // empty prefix, not qualified
                ns = string.Empty;
            }
            else
            {
                ns = _thisNSResolver.LookupNamespace(attrPrefix)!;
            }
 
            return GetAttributePSVI(attrLocalName, ns);
        }
 
        private AttributePSVIInfo? GetAttributePSVI(string localName, string ns)
        {
            Debug.Assert(_coreReaderNameTable.Get(localName) != null);
            Debug.Assert(_coreReaderNameTable.Get(ns) != null);
            AttributePSVIInfo? attInfo;
 
            for (int i = 0; i < _coreReaderAttributeCount; i++)
            {
                attInfo = _attributePSVINodes[i];
                if (attInfo != null)
                {
                    // Will be null for invalid attributes
                    if (Ref.Equal(localName, attInfo.localName) && Ref.Equal(ns, attInfo.namespaceUri))
                    {
                        _currentAttrIndex = i;
                        return attInfo;
                    }
                }
            }
 
            return null;
        }
 
        private ValidatingReaderNodeData? GetDefaultAttribute(string name, bool updatePosition)
        {
            string attrLocalName;
            string attrPrefix;
            ValidateNames.SplitQName(name, out attrPrefix, out attrLocalName);
 
            // Atomize
            attrPrefix = _coreReaderNameTable.Add(attrPrefix);
            attrLocalName = _coreReaderNameTable.Add(attrLocalName);
            string ns;
            if (attrPrefix.Length == 0)
            {
                ns = string.Empty;
            }
            else
            {
                ns = _thisNSResolver.LookupNamespace(attrPrefix)!;
            }
 
            return GetDefaultAttribute(attrLocalName, ns, updatePosition);
        }
 
        private ValidatingReaderNodeData? GetDefaultAttribute(string attrLocalName, string ns, bool updatePosition)
        {
            Debug.Assert(_coreReaderNameTable.Get(attrLocalName) != null);
            Debug.Assert(_coreReaderNameTable.Get(ns) != null);
            ValidatingReaderNodeData? defaultNode;
 
            for (int i = 0; i < _defaultAttributes.Count; i++)
            {
                defaultNode = (ValidatingReaderNodeData)_defaultAttributes[i]!;
                if (Ref.Equal(defaultNode.LocalName, attrLocalName) && Ref.Equal(defaultNode.Namespace, ns))
                {
                    if (updatePosition)
                    {
                        _currentAttrIndex = _coreReader.AttributeCount + i;
                    }
 
                    return defaultNode;
                }
            }
 
            return null;
        }
 
        private AttributePSVIInfo AddAttributePSVI(int attIndex)
        {
            Debug.Assert(attIndex < _attributePSVINodes.Length);
            AttributePSVIInfo attInfo = _attributePSVINodes[attIndex];
            if (attInfo != null)
            {
                attInfo.Reset();
                return attInfo;
            }
 
            if (attIndex >= _attributePSVINodes.Length - 1)
            {
                // reached capacity of PSVIInfo array, Need to increase capacity to twice the initial
                AttributePSVIInfo[] newPSVINodes = new AttributePSVIInfo[_attributePSVINodes.Length * 2];
                Array.Copy(_attributePSVINodes, newPSVINodes, _attributePSVINodes.Length);
                _attributePSVINodes = newPSVINodes;
            }
 
            return _attributePSVINodes[attIndex] ??= new AttributePSVIInfo();
        }
 
        private bool IsXSDRoot(string localName, string ns)
        {
            return Ref.Equal(ns, _nsXs) && Ref.Equal(localName, _xsdSchema);
        }
 
        private void ProcessInlineSchema()
        {
            Debug.Assert(_inlineSchemaParser != null);
            if (_coreReader.Read())
            {
                if (_coreReader.NodeType == XmlNodeType.Element)
                {
                    _attributeCount = _coreReaderAttributeCount = _coreReader.AttributeCount;
                }
                else
                {
                    // Clear attributes info if nodeType is not element
                    ClearAttributesInfo();
                }
 
                if (!_inlineSchemaParser.ParseReaderNode())
                {
                    _inlineSchemaParser.FinishParsing();
                    XmlSchema schema = _inlineSchemaParser.XmlSchema!;
                    _validator.AddSchema(schema);
                    _inlineSchemaParser = null;
                    _validationState = ValidatingReaderState.Read;
                }
            }
        }
 
        private object InternalReadContentAsObject()
        {
            return InternalReadContentAsObject(false);
        }
 
        private object InternalReadContentAsObject(bool unwrapTypedValue)
        {
            return InternalReadContentAsObject(unwrapTypedValue, out _);
        }
 
        private object InternalReadContentAsObject(bool unwrapTypedValue, out string originalStringValue)
        {
            XmlNodeType nodeType = this.NodeType;
            if (nodeType == XmlNodeType.Attribute)
            {
                originalStringValue = this.Value;
                if (_attributePSVI != null && _attributePSVI.typedAttributeValue != null)
                {
                    if (_validationState == ValidatingReaderState.OnDefaultAttribute)
                    {
                        XmlSchemaAttribute schemaAttr = _attributePSVI.attributeSchemaInfo.SchemaAttribute!;
                        originalStringValue = schemaAttr.DefaultValue ?? schemaAttr.FixedValue!;
                    }
 
                    return ReturnBoxedValue(_attributePSVI.typedAttributeValue, AttributeSchemaInfo.XmlType!, unwrapTypedValue);
                }
                else
                {
                    // return string value
                    return this.Value;
                }
            }
            else if (nodeType == XmlNodeType.EndElement)
            {
                if (_atomicValue != null)
                {
                    originalStringValue = _originalAtomicValueString!;
 
                    return _atomicValue;
                }
                else
                {
                    originalStringValue = string.Empty;
 
                    return string.Empty;
                }
            }
            else
            {
                // Positioned on text, CDATA, PI, Comment etc
                if (_validator.CurrentContentType == XmlSchemaContentType.TextOnly)
                {
                    // if current element is of simple type
                    object? value = ReturnBoxedValue(ReadTillEndElement(), _xmlSchemaInfo.XmlType!, unwrapTypedValue);
                    originalStringValue = _originalAtomicValueString!;
 
                    return value;
                }
                else
                {
                    if (_coreReader is XsdCachingReader cachingReader)
                    {
                        originalStringValue = cachingReader.ReadOriginalContentAsString();
                    }
                    else
                    {
                        originalStringValue = InternalReadContentAsString();
                    }
 
                    return originalStringValue;
                }
            }
        }
 
        private object? InternalReadElementContentAsObject(out XmlSchemaType? xmlType)
        {
            return InternalReadElementContentAsObject(out xmlType, false);
        }
 
        private object? InternalReadElementContentAsObject(out XmlSchemaType? xmlType, bool unwrapTypedValue)
        {
            return InternalReadElementContentAsObject(out xmlType, unwrapTypedValue, out _);
        }
 
        private object? InternalReadElementContentAsObject(out XmlSchemaType? xmlType, bool unwrapTypedValue, out string? originalString)
        {
            Debug.Assert(this.NodeType == XmlNodeType.Element);
            object? typedValue;
            xmlType = null;
 
            // If its an empty element, can have default/fixed value
            if (this.IsEmptyElement)
 
            {
                if (_xmlSchemaInfo.ContentType == XmlSchemaContentType.TextOnly)
                {
                    typedValue = ReturnBoxedValue(_atomicValue, _xmlSchemaInfo.XmlType!, unwrapTypedValue);
                }
                else
                {
                    typedValue = _atomicValue;
                }
 
                originalString = _originalAtomicValueString;
                xmlType = ElementXmlType; // Set this for default values
                this.Read();
 
                return typedValue;
            }
 
            // move to content and read typed value
            this.Read();
 
            if (this.NodeType == XmlNodeType.EndElement)
            {
                // If IsDefault is true, the next node will be EndElement
                if (_xmlSchemaInfo.IsDefault)
                {
                    if (_xmlSchemaInfo.ContentType == XmlSchemaContentType.TextOnly)
                    {
                        typedValue = ReturnBoxedValue(_atomicValue, _xmlSchemaInfo.XmlType!, unwrapTypedValue);
                    }
                    else
                    {
                        // anyType has default value
                        typedValue = _atomicValue;
                    }
 
                    originalString = _originalAtomicValueString;
                }
                else
                {
                    // Empty content
                    typedValue = string.Empty;
                    originalString = string.Empty;
                }
            }
            else if (this.NodeType == XmlNodeType.Element)
            {
                // the first child is again element node
                throw new XmlException(SR.Xml_MixedReadElementContentAs, string.Empty, this as IXmlLineInfo);
            }
            else
            {
                typedValue = InternalReadContentAsObject(unwrapTypedValue, out originalString);
 
                // ReadElementContentAsXXX cannot be called on mixed content, if positioned on node other than EndElement, Error
                if (this.NodeType != XmlNodeType.EndElement)
                {
                    throw new XmlException(SR.Xml_MixedReadElementContentAs, string.Empty, this as IXmlLineInfo);
                }
            }
 
            xmlType = ElementXmlType; // Set this as we are moving ahead to the next node
 
            // move to next node
            this.Read();
 
            return typedValue;
        }
 
        private object? ReadTillEndElement()
        {
            if (_atomicValue == null)
            {
                while (_coreReader.Read())
                {
                    if (_replayCache)
                    {
                        // If replaying nodes in the cache, they have already been validated
                        continue;
                    }
 
                    switch (_coreReader.NodeType)
                    {
                        case XmlNodeType.Element:
                            ProcessReaderEvent();
                            goto breakWhile;
 
                        case XmlNodeType.Text:
                        case XmlNodeType.CDATA:
                            _validator.ValidateText(_valueGetter);
                            break;
 
                        case XmlNodeType.Whitespace:
                        case XmlNodeType.SignificantWhitespace:
                            _validator.ValidateWhitespace(_valueGetter);
                            break;
 
                        case XmlNodeType.Comment:
                        case XmlNodeType.ProcessingInstruction:
                            break;
 
                        case XmlNodeType.EndElement:
                            _atomicValue = _validator.ValidateEndElement(_xmlSchemaInfo);
                            _originalAtomicValueString = GetOriginalAtomicValueStringOfElement();
                            if (_manageNamespaces)
                            {
                                Debug.Assert(_nsManager != null);
                                _nsManager.PopScope();
                            }
 
                            goto breakWhile;
                    }
 
                    continue;
                breakWhile:
                    break;
                }
            }
            else
            {
                // atomicValue != null, meaning already read ahead - Switch reader
                if (_atomicValue == this)
                {
                    // switch back invalid marker; dont need it since coreReader moved to endElement
                    _atomicValue = null;
                }
 
                SwitchReader();
            }
 
            return _atomicValue;
        }
 
        private void SwitchReader()
        {
            if (_coreReader is XsdCachingReader cachingReader)
            {
                // Switch back without going over the cached contents again.
                _coreReader = cachingReader.GetCoreReader();
            }
 
            Debug.Assert(_coreReader.NodeType == XmlNodeType.EndElement);
            _replayCache = false;
        }
 
        private void ReadAheadForMemberType()
        {
            while (_coreReader.Read())
            {
                switch (_coreReader.NodeType)
                {
                    case XmlNodeType.Element:
                        Debug.Fail("Should not happen as the caching reader does not cache elements in simple content");
                        break;
 
                    case XmlNodeType.Text:
                    case XmlNodeType.CDATA:
                        _validator.ValidateText(_valueGetter);
                        break;
 
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        _validator.ValidateWhitespace(_valueGetter);
                        break;
 
                    case XmlNodeType.Comment:
                    case XmlNodeType.ProcessingInstruction:
                        break;
 
                    case XmlNodeType.EndElement:
                        _atomicValue = _validator.ValidateEndElement(_xmlSchemaInfo); //?? pop namespaceManager scope
                        _originalAtomicValueString = GetOriginalAtomicValueStringOfElement();
                        if (_atomicValue == null)
                        {
                            // Invalid marker
                            _atomicValue = this;
                        }
                        else if (_xmlSchemaInfo.IsDefault)
                        {
                            // The atomicValue returned is a default value
                            Debug.Assert(_cachingReader != null);
                            _cachingReader.SwitchTextNodeAndEndElement(_xmlSchemaInfo.XmlType!.ValueConverter.ToString(_atomicValue), _originalAtomicValueString);
                        }
 
                        goto breakWhile;
                }
 
                continue;
            breakWhile:
                break;
            }
        }
 
        private void GetIsDefault()
        {
            XsdCachingReader? cachedReader = _coreReader as XsdCachingReader;
            if (cachedReader == null && _xmlSchemaInfo.HasDefaultValue)
            {
                // Get Isdefault
                _coreReader = GetCachingReader();
                Debug.Assert(_cachingReader != null);
                if (_xmlSchemaInfo.IsUnionType && !_xmlSchemaInfo.IsNil)
                {
                    // If it also union, get the memberType as well
                    ReadAheadForMemberType();
                }
                else
                {
                    if (_coreReader.Read())
                    {
                        switch (_coreReader.NodeType)
                        {
                            case XmlNodeType.Element:
                                Debug.Fail("Should not happen as the caching reader does not cache elements in simple content");
                                break;
 
                            case XmlNodeType.Text:
                            case XmlNodeType.CDATA:
                                _validator.ValidateText(_valueGetter);
                                break;
 
                            case XmlNodeType.Whitespace:
                            case XmlNodeType.SignificantWhitespace:
                                _validator.ValidateWhitespace(_valueGetter);
                                break;
 
                            case XmlNodeType.Comment:
                            case XmlNodeType.ProcessingInstruction:
                                break;
 
                            case XmlNodeType.EndElement:
                                _atomicValue = _validator.ValidateEndElement(_xmlSchemaInfo); //?? pop namespaceManager scope
                                _originalAtomicValueString = GetOriginalAtomicValueStringOfElement();
                                if (_xmlSchemaInfo.IsDefault)
                                {
                                    // The atomicValue returned is a default value
                                    _cachingReader.SwitchTextNodeAndEndElement(_xmlSchemaInfo.XmlType!.ValueConverter.ToString(_atomicValue!), _originalAtomicValueString);
                                }
 
                                break;
 
                            default:
                                break;
                        }
                    }
                }
 
                _cachingReader.SetToReplayMode();
                _replayCache = true;
            }
        }
 
        private void GetMemberType()
        {
            if (_xmlSchemaInfo.MemberType != null || _atomicValue == this)
            {
                return;
            }
 
            XsdCachingReader? cachedReader = _coreReader as XsdCachingReader;
            if (cachedReader == null && _xmlSchemaInfo.IsUnionType && !_xmlSchemaInfo.IsNil)
            {
                _coreReader = GetCachingReader();
                Debug.Assert(_cachingReader != null);
                ReadAheadForMemberType();
                _cachingReader.SetToReplayMode();
                _replayCache = true;
            }
        }
 
        private object ReturnBoxedValue(object? typedValue, XmlSchemaType xmlType, bool unWrap)
        {
            if (typedValue != null)
            {
                if (unWrap)
                {
                    // convert XmlAtomicValue[] to object[] for list of unions; The other cases return typed value of the valueType anyway
                    Debug.Assert(xmlType != null && xmlType.Datatype != null);
                    if (xmlType.Datatype.Variety == XmlSchemaDatatypeVariety.List)
                    {
                        Datatype_List? listType = xmlType.Datatype as Datatype_List;
                        Debug.Assert(listType != null);
                        if (listType.ItemType.Variety == XmlSchemaDatatypeVariety.Union)
                        {
                            typedValue = xmlType.ValueConverter.ChangeType(typedValue, xmlType.Datatype.ValueType, _thisNSResolver);
                        }
                    }
                }
 
                return typedValue;
            }
            else
            {
                // return the original string value of the element or attribute
                Debug.Assert(NodeType != XmlNodeType.Attribute);
                typedValue = _validator.GetConcatenatedValue();
            }
 
            return typedValue;
        }
 
        private XsdCachingReader GetCachingReader()
        {
            if (_cachingReader == null)
            {
                _cachingReader = new XsdCachingReader(_coreReader, _lineInfo, new CachingEventHandler(CachingCallBack));
            }
            else
            {
                _cachingReader.Reset(_coreReader);
            }
 
            _lineInfo = _cachingReader as IXmlLineInfo;
            return _cachingReader;
        }
 
        internal ValidatingReaderNodeData CreateDummyTextNode(string attributeValue, int depth)
        {
            _textNode ??= new ValidatingReaderNodeData(XmlNodeType.Text);
 
            _textNode.Depth = depth;
            _textNode.RawValue = attributeValue;
            return _textNode;
        }
 
        internal void CachingCallBack(XsdCachingReader cachingReader)
        {
            _coreReader = cachingReader.GetCoreReader(); // re-switch the core-reader after caching reader is done
            _lineInfo = cachingReader.GetLineInfo();
            _replayCache = false;
        }
 
        private string? GetOriginalAtomicValueStringOfElement()
        {
            if (_xmlSchemaInfo.IsDefault)
            {
                XmlSchemaElement? schemaElem = _xmlSchemaInfo.SchemaElement;
                if (schemaElem != null)
                {
                    return schemaElem.DefaultValue ?? schemaElem.FixedValue;
                }
            }
            else
            {
                return _validator.GetConcatenatedValue();
            }
 
            return string.Empty;
        }
    }
}