File: System\Xml\Core\XsdValidatingReaderAsync.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)

using System.IO;
using System.Text;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Diagnostics;
using System.Globalization;
using System.Collections;
using System.Security.Policy;
using System.Collections.Generic;
using System.Runtime.Versioning;
 
using System.Threading.Tasks;
 
namespace System.Xml {
 
    internal partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver {
 
        // Gets the text value of the current node.
        public override Task<string> GetValueAsync() {
            if ((int)validationState < 0) {
                return Task.FromResult(cachedNode.RawValue);
            }
            return coreReader.GetValueAsync();
        }
 
        public override Task< object > ReadContentAsObjectAsync() {
            if (!CanReadContentAs(this.NodeType)) {
                throw CreateReadContentAsException("ReadContentAsObject");
            }
 
            return InternalReadContentAsObjectAsync(true);
 
        }
 
        public override async Task< string > ReadContentAsStringAsync() {
            if (!CanReadContentAs(this.NodeType)) {
                throw CreateReadContentAsException("ReadContentAsString");
            }
            object typedValue = await InternalReadContentAsObjectAsync().ConfigureAwait(false);
            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(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (FormatException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (OverflowException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
        }
 
        public override async Task< object > ReadContentAsAsync(Type returnType, IXmlNamespaceResolver namespaceResolver) {
            if (!CanReadContentAs(this.NodeType)) {
                throw CreateReadContentAsException("ReadContentAs");
            }
            string originalStringValue;
 
            var tuple_0 = await InternalReadContentAsObjectTupleAsync(false).ConfigureAwait(false);
            originalStringValue = tuple_0.Item1;
 
            object typedValue = tuple_0.Item2;
 
            XmlSchemaType xmlType = NodeType == XmlNodeType.Attribute ? AttributeXmlType : ElementXmlType; //
            try {
                if (xmlType != null) {
                    // special-case convertions 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(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (OverflowException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
        }
 
        public override async Task< object > ReadElementContentAsObjectAsync() {
            if (this.NodeType != XmlNodeType.Element) {
                throw CreateReadElementContentAsException("ReadElementContentAsObject");
            }
 
            var tuple_1 = await InternalReadElementContentAsObjectAsync( true).ConfigureAwait(false);
 
            return tuple_1.Item2;
 
        }
 
        public override async Task< string > ReadElementContentAsStringAsync() {
            if (this.NodeType != XmlNodeType.Element) {
                throw CreateReadElementContentAsException("ReadElementContentAsString");
            }
            XmlSchemaType xmlType;
 
            var tuple_9 = await InternalReadElementContentAsObjectAsync().ConfigureAwait(false);
            xmlType = tuple_9.Item1;
 
            object typedValue = tuple_9.Item2;
 
            try {
                if (xmlType != null) {
                    return xmlType.ValueConverter.ToString(typedValue);
                }
                else {
                    return typedValue as string;
                }
            }
            catch (InvalidCastException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (FormatException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
            catch (OverflowException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, "String", e, this as IXmlLineInfo);
            }
        }
 
        public override async Task< object > ReadElementContentAsAsync(Type returnType, IXmlNamespaceResolver namespaceResolver) {
            if (this.NodeType != XmlNodeType.Element) {
                throw CreateReadElementContentAsException("ReadElementContentAs");
            }
            XmlSchemaType xmlType;
            string originalStringValue;
 
            var tuple_10 = await InternalReadElementContentAsObjectTupleAsync( false).ConfigureAwait(false);
            xmlType = tuple_10.Item1;
            originalStringValue = tuple_10.Item2;
 
            object typedValue = tuple_10.Item3;
 
            try {
                if (xmlType != null) {
                    // special-case convertions 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(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (InvalidCastException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
            catch (OverflowException e) {
                throw new XmlException(Res.Xml_ReadContentAsFormatException, returnType.ToString(), e, this as IXmlLineInfo);
            }
        }
 
        private Task<bool> ReadAsync_Read(Task<bool> task) {
            if (task.IsSuccess()) {
                if (task.Result) {
                    return ProcessReaderEventAsync().ReturnTaskBoolWhenFinish(true);
                }
                else {
                    validator.EndValidation();
                    if (coreReader.EOF) {
                        validationState = ValidatingReaderState.EOF;
                    }
                    return AsyncHelper.DoneTaskFalse;
                }
            }
            else {
                return _ReadAsync_Read(task);
            }
        }
 
        private async Task<bool> _ReadAsync_Read(Task<bool> task) {
 
            if (await task.ConfigureAwait(false)) {
                await ProcessReaderEventAsync().ConfigureAwait(false);
                return true;
            }
            else {
                validator.EndValidation();
                if (coreReader.EOF) {
                    validationState = ValidatingReaderState.EOF;
                }
                return false;
            }
        }
 
        private Task<bool> ReadAsync_ReadAhead(Task task) {
            if (task.IsSuccess()) {
                validationState = ValidatingReaderState.Read;
                return AsyncHelper.DoneTaskTrue; ;
            }
            else {
                return _ReadAsync_ReadAhead(task);
            }
        }
 
        private async Task<bool> _ReadAsync_ReadAhead(Task task) {
            await task.ConfigureAwait(false);
            validationState = ValidatingReaderState.Read;
            return true;
        }
 
        // Reads the next node from the stream/TextReader.
        public override Task< bool > ReadAsync() {
            switch (validationState) {
                case ValidatingReaderState.Read:
                    Task<bool> readTask = coreReader.ReadAsync();
                    return ReadAsync_Read(readTask);
 
                case ValidatingReaderState.ParseInlineSchema:
                    return ProcessInlineSchemaAsync().ReturnTaskBoolWhenFinish(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();
                    Task task = ProcessReaderEventAsync();
                    return ReadAsync_ReadAhead(task);
 
                case ValidatingReaderState.OnReadBinaryContent:
                    validationState = savedState;
                    return readBinaryHelper.FinishAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
 
                case ValidatingReaderState.Init:
                    validationState = ValidatingReaderState.Read;
                    if (coreReader.ReadState == ReadState.Interactive) { //If the underlying reader is already positioned on a ndoe, process it
                        return ProcessReaderEventAsync().ReturnTaskBoolWhenFinish(true);
                    }
                    else {
                        goto case ValidatingReaderState.Read;
                    }
 
                case ValidatingReaderState.ReaderClosed:
                case ValidatingReaderState.EOF:
                    return AsyncHelper.DoneTaskFalse;
 
                default:
                    return AsyncHelper.DoneTaskFalse;
            }
        }
 
        // Skips to the end tag of the current element.
        public override async Task SkipAsync() {
            int startDepth = Depth;
            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;
                    }
                    await coreReader.SkipAsync().ConfigureAwait(false);
                    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()
            await ReadAsync().ConfigureAwait(false);
            return;
        }
 
        public override async Task< int > ReadContentAsBase64Async(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
            int readCount = await readBinaryHelper.ReadContentAsBase64Async(buffer, index, count).ConfigureAwait(false);
 
            // set OnReadBinaryContent state again and return
            savedState = validationState;
            validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override async Task< int > ReadContentAsBinHexAsync(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
            int readCount = await readBinaryHelper.ReadContentAsBinHexAsync(buffer, index, count).ConfigureAwait(false);
 
            // set OnReadBinaryContent state again and return
            savedState = validationState;
            validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override async Task< int > ReadElementContentAsBase64Async(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
            int readCount = await readBinaryHelper.ReadElementContentAsBase64Async(buffer, index, count).ConfigureAwait(false);
 
            // set OnReadBinaryContent state again and return
            savedState = validationState;
            validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        public override async Task< int > ReadElementContentAsBinHexAsync(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
            int readCount = await readBinaryHelper.ReadElementContentAsBinHexAsync(buffer, index, count).ConfigureAwait(false);
 
            // set OnReadBinaryContent state again and return
            savedState = validationState;
            validationState = ValidatingReaderState.OnReadBinaryContent;
            return readCount;
        }
 
        private Task ProcessReaderEventAsync() {
            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 AsyncHelper.DoneTask;
 
            }
            switch (coreReader.NodeType) {
                case XmlNodeType.Element:
 
                    return ProcessElementEventAsync();
 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    validator.ValidateWhitespace(GetStringValue);
                    break;
 
                case XmlNodeType.Text:          // text inside a node
                case XmlNodeType.CDATA:         // <![CDATA[...]]>
                    validator.ValidateText(GetStringValue);
                    break;
 
                case XmlNodeType.EndElement:
 
                    return ProcessEndElementEventAsync();
 
                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;
            }
 
            return AsyncHelper.DoneTask;
 
        }
 
        // SxS: This function calls ValidateElement on XmlSchemaValidator which is annotated with ResourceExposure attribute.
        // Since the resource names (namespace location) are not provided directly by the user (they are read from the source
        // document) and the function does not expose any resources it is fine to suppress the SxS warning. 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
        private async Task ProcessElementEventAsync() {
            if (this.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);
                    await inlineSchemaParser.StartParsingAsync(coreReader, null).ConfigureAwait(false);
                    inlineSchemaParser.ParseReaderNode();
                    validationState = ValidatingReaderState.ParseInlineSchema;
                }
                else {
                    validationState = ValidatingReaderState.ClearAttributes;
                }
            }
            else { //Validate element
 
                //Clear previous data
                atomicValue = null;
                originalAtomicValueString = null;
                xmlSchemaInfo.Clear();
 
                if (manageNamespaces) {
                    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)) {
                            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) {
                    await ProcessEndElementEventAsync().ConfigureAwait(false);
                }
                validationState = ValidatingReaderState.ClearAttributes;
            }
        }
 
        private async Task ProcessEndElementEventAsync() {
            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();
                cachingReader.RecordTextNode( xmlSchemaInfo.XmlType.ValueConverter.ToString( atomicValue ), originalAtomicValueString, depth + 1, 0, 0 );
                cachingReader.RecordEndElementNode(); 
                await cachingReader.SetToReplayModeAsync().ConfigureAwait(false);
                replayCache = true;
            }
            else if (manageNamespaces) {
                nsManager.PopScope();
            }
        }
 
        private async Task ProcessInlineSchemaAsync() {
            Debug.Assert(inlineSchemaParser != null);
            if (await coreReader.ReadAsync().ConfigureAwait(false)) {
                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 Task< object > InternalReadContentAsObjectAsync() {
            return InternalReadContentAsObjectAsync(false);
        }
 
        private async Task< object > InternalReadContentAsObjectAsync(bool unwrapTypedValue) {
 
            var tuple_11 = await InternalReadContentAsObjectTupleAsync(unwrapTypedValue).ConfigureAwait(false);
 
            return tuple_11.Item2;
 
        }
 
        private async Task< Tuple<string, object> > InternalReadContentAsObjectTupleAsync(bool unwrapTypedValue) {
            Tuple<string, object> tuple;
            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 != null ) ? schemaAttr.DefaultValue : schemaAttr.FixedValue;
                    }
 
                    tuple = new Tuple<string, object>(originalStringValue, ReturnBoxedValue( attributePSVI.typedAttributeValue, AttributeSchemaInfo.XmlType, unwrapTypedValue ));
                    return tuple;
 
                }
                else { //return string value
 
                    tuple = new Tuple<string, object>(originalStringValue, this.Value);
                    return tuple;
 
                }
            }
            else if (nodeType == XmlNodeType.EndElement) {
                if (atomicValue != null) {
                    originalStringValue = originalAtomicValueString;
 
                    tuple = new Tuple<string, object>(originalStringValue, atomicValue);
                    return tuple;
 
                }
                else {
                    originalStringValue = string.Empty;
 
                    tuple = new Tuple<string, object>(originalStringValue, string.Empty);
                    return tuple;
 
                }
            }
            else { //Positioned on text, CDATA, PI, Comment etc
                if (validator.CurrentContentType == XmlSchemaContentType.TextOnly) {  //if current element is of simple type
                    object value = ReturnBoxedValue(await ReadTillEndElementAsync().ConfigureAwait(false), xmlSchemaInfo.XmlType, unwrapTypedValue);
                    originalStringValue = originalAtomicValueString;
 
                    tuple = new Tuple<string, object>(originalStringValue, value);
                    return tuple;
 
                }
                else {
                    XsdCachingReader cachingReader = this.coreReader as XsdCachingReader;
                    if ( cachingReader != null ) {
                        originalStringValue = cachingReader.ReadOriginalContentAsString();
                    }
                    else {
                        originalStringValue = await InternalReadContentAsStringAsync().ConfigureAwait(false);
                    }
 
                    tuple = new Tuple<string, object>(originalStringValue, originalStringValue);
                    return tuple;
 
                }
            }
        }
 
        private Task< Tuple<XmlSchemaType, object> > InternalReadElementContentAsObjectAsync() {
 
            return InternalReadElementContentAsObjectAsync( false);
 
        }
 
        private async Task< Tuple<XmlSchemaType, object> > InternalReadElementContentAsObjectAsync(bool unwrapTypedValue) {
 
            var tuple_13 = await InternalReadElementContentAsObjectTupleAsync( unwrapTypedValue).ConfigureAwait(false);
 
            return new Tuple<XmlSchemaType, object>(tuple_13.Item1, tuple_13.Item3);
 
        }
 
        private async Task< Tuple<XmlSchemaType, string, object> > InternalReadElementContentAsObjectTupleAsync(bool unwrapTypedValue) {
            Tuple<XmlSchemaType, string, object> tuple;
            XmlSchemaType xmlType;
            string originalString;
 
            Debug.Assert(this.NodeType == XmlNodeType.Element);
            object typedValue = null;
            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 
                await this.ReadAsync().ConfigureAwait(false);
 
                tuple = new Tuple<XmlSchemaType, string, object>(xmlType, originalString, typedValue);
                return tuple;
 
            }
            // move to content and read typed value
            await this.ReadAsync().ConfigureAwait(false);
 
            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(Res.Xml_MixedReadElementContentAs, string.Empty, this as IXmlLineInfo);
            }
            else {
 
                var tuple_14 = await InternalReadContentAsObjectTupleAsync(unwrapTypedValue).ConfigureAwait(false);
                originalString = tuple_14.Item1;
 
                typedValue = tuple_14.Item2;
 
                // ReadElementContentAsXXX cannot be called on mixed content, if positioned on node other than EndElement, Error
                if (this.NodeType != XmlNodeType.EndElement) {
                    throw new XmlException(Res.Xml_MixedReadElementContentAs, string.Empty, this as IXmlLineInfo);
                }
            }
            xmlType = ElementXmlType; //Set this as we are moving ahead to the next node
 
            // move to next node
            await this.ReadAsync().ConfigureAwait(false);
 
            tuple = new Tuple<XmlSchemaType, string, object>(xmlType, originalString, typedValue);
            return tuple;
 
        }
 
        private async Task< object > ReadTillEndElementAsync() {
            if (atomicValue == null) {
                while (await coreReader.ReadAsync().ConfigureAwait(false)) {
                    if (replayCache) { //If replaying nodes in the cache, they have already been validated
                        continue;
                    }
                    switch (coreReader.NodeType) {
                        case XmlNodeType.Element:
                            await ProcessReaderEventAsync().ConfigureAwait(false);
                            goto breakWhile;
 
                        case XmlNodeType.Text:
                        case XmlNodeType.CDATA:
                            validator.ValidateText(GetStringValue);
                            break;
 
                        case XmlNodeType.Whitespace:
                        case XmlNodeType.SignificantWhitespace:
                            validator.ValidateWhitespace(GetStringValue);
                            break;
 
                        case XmlNodeType.Comment:
                        case XmlNodeType.ProcessingInstruction:
                            break;
 
                        case XmlNodeType.EndElement:
                            atomicValue = validator.ValidateEndElement(xmlSchemaInfo);
                            originalAtomicValueString = GetOriginalAtomicValueStringOfElement();
                            if (manageNamespaces) {
                                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;
        }
 
    }
}