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

using System.Threading.Tasks;
 
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.Collections;
using System.Globalization;
using System.Collections.Generic;
 
// OpenIssue : is it better to cache the current namespace decls for each elem
//  as the current code does, or should it just always walk the namespace stack?
 
namespace System.Xml {
 
    internal partial class XmlWellFormedWriter : XmlWriter {
 
        public override Task WriteStartDocumentAsync() {
            return WriteStartDocumentImplAsync(XmlStandalone.Omit);
        }
 
        public override Task WriteStartDocumentAsync(bool standalone) {
            return WriteStartDocumentImplAsync(standalone ? XmlStandalone.Yes : XmlStandalone.No);
        }
 
        public override async Task WriteEndDocumentAsync() {
            try {
                // auto-close all elements
                while (elemTop > 0) {
                    await WriteEndElementAsync().ConfigureAwait(false);
                }
                State prevState = currentState;
                await AdvanceStateAsync(Token.EndDocument).ConfigureAwait(false);
 
                if (prevState != State.AfterRootEle) {
                    throw new ArgumentException(Res.GetString(Res.Xml_NoRoot));
                }
                if (rawWriter == null) {
                    await writer.WriteEndDocumentAsync().ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteDocTypeAsync(string name, string pubid, string sysid, string subset) {
            try {
                if (name == null || name.Length == 0) {
                    throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
                }
                XmlConvert.VerifyQName(name, ExceptionType.XmlException);
 
                if (conformanceLevel == ConformanceLevel.Fragment) {
                    throw new InvalidOperationException(Res.GetString(Res.Xml_DtdNotAllowedInFragment));
                }
 
                await AdvanceStateAsync(Token.Dtd).ConfigureAwait(false);
                if (dtdWritten) {
                    currentState = State.Error;
                    throw new InvalidOperationException(Res.GetString(Res.Xml_DtdAlreadyWritten));
                }
 
                if (conformanceLevel == ConformanceLevel.Auto) {
                    conformanceLevel = ConformanceLevel.Document;
                    stateTable = StateTableDocument;
                }
 
                int i;
 
                // check characters
                if (checkCharacters) {
                    if (pubid != null) {
                        if ((i = xmlCharType.IsPublicId(pubid)) >= 0) {
                            throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(pubid, i)), "pubid");
                        }
                    }
                    if (sysid != null) {
                        if ((i = xmlCharType.IsOnlyCharData(sysid)) >= 0) {
                            throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(sysid, i)), "sysid");
                        }
                    }
                    if (subset != null) {
                        if ((i = xmlCharType.IsOnlyCharData(subset)) >= 0) {
                            throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(subset, i)), "subset");
                        }
                    }
                }
 
                // write doctype
                await writer.WriteDocTypeAsync(name, pubid, sysid, subset).ConfigureAwait(false);
                dtdWritten = true;
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        //check if any exception before return the task
        private Task TryReturnTask(Task task) {
            if (task.IsSuccess()) {
                return AsyncHelper.DoneTask;
            }
            else {
                return _TryReturnTask(task);
            }
        }
 
        private async Task _TryReturnTask(Task task) {
            try {
                await task.ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        //call nextTaskFun after task finish. Check exception.
        private Task SequenceRun(Task task, Func<Task> nextTaskFun) {
            if (task.IsSuccess()) {
                return TryReturnTask( nextTaskFun() );
            }
            else {
                return _SequenceRun(task, nextTaskFun);
            }
        }
 
        private async Task _SequenceRun(Task task, Func<Task> nextTaskFun) {
            try {
                await task.ConfigureAwait(false);
                await nextTaskFun().ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override Task WriteStartElementAsync(string prefix, string localName, string ns) {
            try {
                // check local name
                if (localName == null || localName.Length == 0) {
                    throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
                }
                CheckNCName(localName);
 
                Task task = AdvanceStateAsync(Token.StartElement);
                if (task.IsSuccess()) {
                    return WriteStartElementAsync_NoAdvanceState(prefix, localName, ns);
                }
                else {
                    return WriteStartElementAsync_NoAdvanceState(task, prefix, localName, ns);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteStartElementAsync_NoAdvanceState(string prefix, string localName, string ns) {
            try {
                // lookup prefix / namespace  
                if (prefix == null) {
                    if (ns != null) {
                        prefix = LookupPrefix(ns);
                    }
                    if (prefix == null) {
                        prefix = string.Empty;
                    }
                }
                else if (prefix.Length > 0) {
                    CheckNCName(prefix);
                    if (ns == null) {
                        ns = LookupNamespace(prefix);
                    }
                    if (ns == null || (ns != null && ns.Length == 0)) {
                        throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
                    }
                }
                if (ns == null) {
                    ns = LookupNamespace(prefix);
                    if (ns == null) {
                        Debug.Assert(prefix.Length == 0);
                        ns = string.Empty;
                    }
                }
 
                if (elemTop == 0 && rawWriter != null) {
                    // notify the underlying raw writer about the root level element
                    rawWriter.OnRootElement(conformanceLevel);
                }
 
                // write start tag
                Task task = writer.WriteStartElementAsync(prefix, localName, ns);
                if (task.IsSuccess()) {
                    WriteStartElementAsync_FinishWrite(prefix, localName, ns);
                }
                else {
                    return WriteStartElementAsync_FinishWrite(task, prefix, localName, ns);
                }
                return AsyncHelper.DoneTask;
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteStartElementAsync_NoAdvanceState(Task task, string prefix, string localName, string ns) {
            try {
                await task.ConfigureAwait(false);
                await WriteStartElementAsync_NoAdvanceState(prefix, localName, ns).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private void WriteStartElementAsync_FinishWrite(string prefix, string localName, string ns) {
            try {
                // push element on stack and add/check namespace
                int top = ++elemTop;
                if (top == elemScopeStack.Length) {
                    ElementScope[] newStack = new ElementScope[top * 2];
                    Array.Copy(elemScopeStack, newStack, top);
                    elemScopeStack = newStack;
                }
                elemScopeStack[top].Set(prefix, localName, ns, nsTop);
 
                PushNamespaceImplicit(prefix, ns);
 
                if (attrCount >= MaxAttrDuplWalkCount) {
                    attrHashTable.Clear();
                }
                attrCount = 0;
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteStartElementAsync_FinishWrite(Task t, string prefix, string localName, string ns) {
            try {
                await t.ConfigureAwait(false);
                WriteStartElementAsync_FinishWrite(prefix, localName, ns);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override Task WriteEndElementAsync() {
            try {
                Task task = AdvanceStateAsync(Token.EndElement);
 
                return SequenceRun(task, WriteEndElementAsync_NoAdvanceState);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteEndElementAsync_NoAdvanceState() {
            try {
                int top = elemTop;
                if (top == 0) {
                    throw new XmlException(Res.Xml_NoStartTag, string.Empty);
                }
                Task task;
                // write end tag
                if (rawWriter != null) {
                    task = elemScopeStack[top].WriteEndElementAsync(rawWriter);
                }
                else {
                    task = writer.WriteEndElementAsync();
                }
 
                return SequenceRun(task, WriteEndElementAsync_FinishWrite);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteEndElementAsync_FinishWrite() {
            try {
                int top = elemTop;
                // pop namespaces
                int prevNsTop = elemScopeStack[top].prevNSTop;
                if (useNsHashtable && prevNsTop < nsTop) {
                    PopNamespaces(prevNsTop + 1, nsTop);
                }
                nsTop = prevNsTop;
                elemTop = --top;
 
                // check "one root element" condition for ConformanceLevel.Document
                if (top == 0) {
                    if (conformanceLevel == ConformanceLevel.Document) {
                        currentState = State.AfterRootEle;
                    }
                    else {
                        currentState = State.TopLevel;
                    }
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
            return AsyncHelper.DoneTask;
        }
 
        public override Task WriteFullEndElementAsync() {
            try {
                Task task = AdvanceStateAsync(Token.EndElement);
 
                return SequenceRun(task, WriteFullEndElementAsync_NoAdvanceState);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteFullEndElementAsync_NoAdvanceState() {
            try {
                int top = elemTop;
                if (top == 0) {
                    throw new XmlException(Res.Xml_NoStartTag, string.Empty);
                }
                Task task;
                // write end tag
                if (rawWriter != null) {
                    task = elemScopeStack[top].WriteFullEndElementAsync(rawWriter);
                }
                else {
                    task = writer.WriteFullEndElementAsync();
                }
 
                return SequenceRun(task, WriteEndElementAsync_FinishWrite);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        protected internal override Task WriteStartAttributeAsync(string prefix, string localName, string namespaceName) {
            try {
                // check local name
                if (localName == null || localName.Length == 0) {
                    if (prefix == "xmlns") {
                        localName = "xmlns";
                        prefix = string.Empty;
                    }
                    else {
                        throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
                    }
                }
                CheckNCName(localName);
 
                Task task = AdvanceStateAsync(Token.StartAttribute);
                if (task.IsSuccess()) {
                    return WriteStartAttributeAsync_NoAdvanceState(prefix, localName, namespaceName);
                }
                else {
                    return WriteStartAttributeAsync_NoAdvanceState(task, prefix, localName, namespaceName);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteStartAttributeAsync_NoAdvanceState(string prefix, string localName, string namespaceName) {
            try {
                // lookup prefix / namespace  
                if (prefix == null) {
                    if (namespaceName != null) {
                        // special case prefix=null/localname=xmlns
                        if (!(localName == "xmlns" && namespaceName == XmlReservedNs.NsXmlNs))
                            prefix = LookupPrefix(namespaceName);
                    }
                    if (prefix == null) {
                        prefix = string.Empty;
                    }
                }
                if (namespaceName == null) {
                    if (prefix != null && prefix.Length > 0) {
                        namespaceName = LookupNamespace(prefix);
                    }
                    if (namespaceName == null) {
                        namespaceName = string.Empty;
                    }
                }
 
                if (prefix.Length == 0) {
                    if (localName[0] == 'x' && localName == "xmlns") {
                        if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXmlNs) {
                            throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
                        }
                        curDeclPrefix = String.Empty;
                        SetSpecialAttribute(SpecialAttribute.DefaultXmlns);
                        goto SkipPushAndWrite;
                    }
                    else if (namespaceName.Length > 0) {
                        prefix = LookupPrefix(namespaceName);
                        if (prefix == null || prefix.Length == 0) {
                            prefix = GeneratePrefix();
                        }
                    }
                }
                else {
                    if (prefix[0] == 'x') {
                        if (prefix == "xmlns") {
                            if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXmlNs) {
                                throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
                            }
                            curDeclPrefix = localName;
                            SetSpecialAttribute(SpecialAttribute.PrefixedXmlns);
                            goto SkipPushAndWrite;
                        }
                        else if (prefix == "xml") {
                            if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXml) {
                                throw new ArgumentException(Res.GetString(Res.Xml_XmlPrefix));
                            }
                            switch (localName) {
                                case "space":
                                    SetSpecialAttribute(SpecialAttribute.XmlSpace);
                                    goto SkipPushAndWrite;
                                case "lang":
                                    SetSpecialAttribute(SpecialAttribute.XmlLang);
                                    goto SkipPushAndWrite;
                            }
                        }
                    }
 
                    CheckNCName(prefix);
 
                    if (namespaceName.Length == 0) {
                        // attributes cannot have default namespace
                        prefix = string.Empty;
                    }
                    else {
                        string definedNs = LookupLocalNamespace(prefix);
                        if (definedNs != null && definedNs != namespaceName) {
                            prefix = GeneratePrefix();
                        }
                    }
                }
 
                if (prefix.Length != 0) {
                    PushNamespaceImplicit(prefix, namespaceName);
                }
 
            SkipPushAndWrite:
 
                // add attribute to the list and check for duplicates
                AddAttribute(prefix, localName, namespaceName);
 
                if (specAttr == SpecialAttribute.No) {
                    // write attribute name
                    return TryReturnTask( writer.WriteStartAttributeAsync(prefix, localName, namespaceName) );
                }
                return AsyncHelper.DoneTask;
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteStartAttributeAsync_NoAdvanceState(Task task, string prefix, string localName, string namespaceName) {
            try {
                await task.ConfigureAwait(false);
                await WriteStartAttributeAsync_NoAdvanceState(prefix, localName, namespaceName).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
 
        protected internal override Task WriteEndAttributeAsync() {
            try {
                Task task = AdvanceStateAsync(Token.EndAttribute);
                return SequenceRun(task, WriteEndAttributeAsync_NoAdvance);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteEndAttributeAsync_NoAdvance() {
            try {
 
                if (specAttr != SpecialAttribute.No) {
                    return WriteEndAttributeAsync_SepcialAtt();
                }
                else {
                    return TryReturnTask( writer.WriteEndAttributeAsync() );
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteEndAttributeAsync_SepcialAtt() {
            try {
 
                string value;
 
                switch (specAttr) {
                    case SpecialAttribute.DefaultXmlns:
                        value = attrValueCache.StringValue;
                        if (PushNamespaceExplicit(string.Empty, value)) { // returns true if the namespace declaration should be written out
                            if (rawWriter != null) {
                                if (rawWriter.SupportsNamespaceDeclarationInChunks) {
                                    await rawWriter.WriteStartNamespaceDeclarationAsync(string.Empty).ConfigureAwait(false);
                                    await attrValueCache.ReplayAsync(rawWriter).ConfigureAwait(false);
                                    await rawWriter.WriteEndNamespaceDeclarationAsync().ConfigureAwait(false);
                                }
                                else {
                                    await rawWriter.WriteNamespaceDeclarationAsync(string.Empty, value).ConfigureAwait(false);
                                }
                            }
                            else {
                                await writer.WriteStartAttributeAsync(string.Empty, "xmlns", XmlReservedNs.NsXmlNs).ConfigureAwait(false);
                                await attrValueCache.ReplayAsync(writer).ConfigureAwait(false);
                                await writer.WriteEndAttributeAsync().ConfigureAwait(false);
                            }
                        }
                        curDeclPrefix = null;
                        break;
                    case SpecialAttribute.PrefixedXmlns:
                        value = attrValueCache.StringValue;
                        if (value.Length == 0) {
                            throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
                        }
                        if (value == XmlReservedNs.NsXmlNs || (value == XmlReservedNs.NsXml && curDeclPrefix != "xml")) {
                            throw new ArgumentException(Res.GetString(Res.Xml_CanNotBindToReservedNamespace));
                        }
                        if (PushNamespaceExplicit(curDeclPrefix, value)) { // returns true if the namespace declaration should be written out
                            if (rawWriter != null) {
                                if (rawWriter.SupportsNamespaceDeclarationInChunks) {
                                    await rawWriter.WriteStartNamespaceDeclarationAsync(curDeclPrefix).ConfigureAwait(false);
                                    await attrValueCache.ReplayAsync(rawWriter).ConfigureAwait(false);
                                    await rawWriter.WriteEndNamespaceDeclarationAsync().ConfigureAwait(false);
                                }
                                else {
                                    await rawWriter.WriteNamespaceDeclarationAsync(curDeclPrefix, value).ConfigureAwait(false);
                                }
                            }
                            else {
                                await writer.WriteStartAttributeAsync("xmlns", curDeclPrefix, XmlReservedNs.NsXmlNs).ConfigureAwait(false);
                                await attrValueCache.ReplayAsync(writer).ConfigureAwait(false);
                                await writer.WriteEndAttributeAsync().ConfigureAwait(false);
                            }
                        }
                        curDeclPrefix = null;
                        break;
                    case SpecialAttribute.XmlSpace:
                        attrValueCache.Trim();
                        value = attrValueCache.StringValue;
 
                        if (value == "default") {
                            elemScopeStack[elemTop].xmlSpace = XmlSpace.Default;
                        }
                        else if (value == "preserve") {
                            elemScopeStack[elemTop].xmlSpace = XmlSpace.Preserve;
                        }
                        else {
                            throw new ArgumentException(Res.GetString(Res.Xml_InvalidXmlSpace, value));
                        }
                        await writer.WriteStartAttributeAsync("xml", "space", XmlReservedNs.NsXml).ConfigureAwait(false);
                        await attrValueCache.ReplayAsync(writer).ConfigureAwait(false);
                        await writer.WriteEndAttributeAsync().ConfigureAwait(false);
                        break;
                    case SpecialAttribute.XmlLang:
                        value = attrValueCache.StringValue;
                        elemScopeStack[elemTop].xmlLang = value;
                        await writer.WriteStartAttributeAsync("xml", "lang", XmlReservedNs.NsXml).ConfigureAwait(false);
                        await attrValueCache.ReplayAsync(writer).ConfigureAwait(false);
                        await writer.WriteEndAttributeAsync().ConfigureAwait(false);
                        break;
                }
                specAttr = SpecialAttribute.No;
                attrValueCache.Clear();
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteCDataAsync(string text) {
            try {
                if (text == null) {
                    text = string.Empty;
                }
                await AdvanceStateAsync(Token.CData).ConfigureAwait(false);
                await writer.WriteCDataAsync(text).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteCommentAsync(string text) {
            try {
                if (text == null) {
                    text = string.Empty;
                }
                await AdvanceStateAsync(Token.Comment).ConfigureAwait(false);
                await writer.WriteCommentAsync(text).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteProcessingInstructionAsync(string name, string text) {
            try {
                // check name
                if (name == null || name.Length == 0) {
                    throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
                }
                CheckNCName(name);
 
                // check text
                if (text == null) {
                    text = string.Empty;
                }
 
                // xml declaration is a special case (not a processing instruction, but we allow WriteProcessingInstruction as a convenience)
                if (name.Length == 3 && string.Compare(name, "xml", StringComparison.OrdinalIgnoreCase) == 0) {
                    if (currentState != State.Start) {
                        throw new ArgumentException(Res.GetString(conformanceLevel == ConformanceLevel.Document ? Res.Xml_DupXmlDecl : Res.Xml_CannotWriteXmlDecl));
                    }
 
                    xmlDeclFollows = true;
                    await AdvanceStateAsync(Token.PI).ConfigureAwait(false);
 
                    if (rawWriter != null) {
                        // Translate PI into an xml declaration
                        await rawWriter.WriteXmlDeclarationAsync(text).ConfigureAwait(false);
                    }
                    else {
                        await writer.WriteProcessingInstructionAsync(name, text).ConfigureAwait(false);
                    }
                }
                else {
                    await AdvanceStateAsync(Token.PI).ConfigureAwait(false);
                    await writer.WriteProcessingInstructionAsync(name, text).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteEntityRefAsync(string name) {
            try {
                // check name
                if (name == null || name.Length == 0) {
                    throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
                }
                CheckNCName(name);
 
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteEntityRef(name);
                }
                else {
                    await writer.WriteEntityRefAsync(name).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteCharEntityAsync(char ch) {
            try {
                if (Char.IsSurrogate(ch)) {
                    throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
                }
 
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteCharEntity(ch);
                }
                else {
                    await writer.WriteCharEntityAsync(ch).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteSurrogateCharEntityAsync(char lowChar, char highChar) {
            try {
                if (!Char.IsSurrogatePair(highChar, lowChar)) {
                    throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
                }
 
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteSurrogateCharEntity(lowChar, highChar);
                }
                else {
                    await writer.WriteSurrogateCharEntityAsync(lowChar, highChar).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteWhitespaceAsync(string ws) {
            try {
                if (ws == null) {
                    ws = string.Empty;
                }
                if (!XmlCharType.Instance.IsOnlyWhitespace(ws)) {
                    throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace));
                }
 
                await AdvanceStateAsync(Token.Whitespace).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteWhitespace(ws);
                }
                else {
                    await writer.WriteWhitespaceAsync(ws).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override Task WriteStringAsync(string text) {
            try {
                if (text == null) {
                    return AsyncHelper.DoneTask;
                }
 
                Task task = AdvanceStateAsync(Token.Text);
 
                if (task.IsSuccess()) {
                    return WriteStringAsync_NoAdvanceState(text);
                }
                else {
                    return WriteStringAsync_NoAdvanceState(task, text);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private Task WriteStringAsync_NoAdvanceState(string text) {
            try {
              
                if (SaveAttrValue) {
                    attrValueCache.WriteString(text);
                    return AsyncHelper.DoneTask;
                }
                else {
                    return TryReturnTask( writer.WriteStringAsync(text) );
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteStringAsync_NoAdvanceState(Task task, string text) {
            try {
                await task.ConfigureAwait(false);
                await WriteStringAsync_NoAdvanceState(text).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteCharsAsync(char[] buffer, int index, int count) {
            try {
                if (buffer == null) {
                    throw new ArgumentNullException("buffer");
                }
                if (index < 0) {
                    throw new ArgumentOutOfRangeException("index");
                }
                if (count < 0) {
                    throw new ArgumentOutOfRangeException("count");
                }
                if (count > buffer.Length - index) {
                    throw new ArgumentOutOfRangeException("count");
                }
 
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteChars(buffer, index, count);
                }
                else {
                    await writer.WriteCharsAsync(buffer, index, count).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteRawAsync(char[] buffer, int index, int count) {
            try {
                if (buffer == null) {
                    throw new ArgumentNullException("buffer");
                }
                if (index < 0) {
                    throw new ArgumentOutOfRangeException("index");
                }
                if (count < 0) {
                    throw new ArgumentOutOfRangeException("count");
                }
                if (count > buffer.Length - index) {
                    throw new ArgumentOutOfRangeException("count");
                }
 
                await AdvanceStateAsync(Token.RawData).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteRaw(buffer, index, count);
                }
                else {
                    await writer.WriteRawAsync(buffer, index, count).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteRawAsync(string data) {
            try {
                if (data == null) {
                    return;
                }
 
                await AdvanceStateAsync(Token.RawData).ConfigureAwait(false);
                if (SaveAttrValue) {
                    attrValueCache.WriteRaw(data);
                }
                else {
                    await writer.WriteRawAsync(data).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override Task WriteBase64Async(byte[] buffer, int index, int count) {
            try {
                if (buffer == null) {
                    throw new ArgumentNullException("buffer");
                }
                if (index < 0) {
                    throw new ArgumentOutOfRangeException("index");
                }
                if (count < 0) {
                    throw new ArgumentOutOfRangeException("count");
                }
                if (count > buffer.Length - index) {
                    throw new ArgumentOutOfRangeException("count");
                }
 
                Task task = AdvanceStateAsync(Token.Base64);
 
                if (task.IsSuccess()) {
                    return TryReturnTask(writer.WriteBase64Async(buffer, index, count));
                }
                else {
                    return WriteBase64Async_NoAdvanceState(task, buffer, index, count);
                }
                
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteBase64Async_NoAdvanceState(Task task, byte[] buffer, int index, int count) {
            try {
 
                await task.ConfigureAwait(false);
                await writer.WriteBase64Async(buffer, index, count).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task FlushAsync() {
            try {
                await writer.FlushAsync().ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteQualifiedNameAsync(string localName, string ns) {
            try {
                if (localName == null || localName.Length == 0) {
                    throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
                }
                CheckNCName(localName);
 
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                string prefix = String.Empty;
                if (ns != null && ns.Length != 0) {
                    prefix = LookupPrefix(ns);
                    if (prefix == null) {
                        if (currentState != State.Attribute) {
                            throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns));
                        }
                        prefix = GeneratePrefix();
                        PushNamespaceImplicit(prefix, ns);
                    }
                }
                // if this is a special attribute, then just convert this to text
                // otherwise delegate to raw-writer
                if (SaveAttrValue || rawWriter == null) {
                    if (prefix.Length != 0) {
                        await WriteStringAsync(prefix).ConfigureAwait(false);
                        await WriteStringAsync(":").ConfigureAwait(false);
                    }
                    await WriteStringAsync(localName).ConfigureAwait(false);
                }
                else {
                    await rawWriter.WriteQualifiedNameAsync(prefix, localName, ns).ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        public override async Task WriteBinHexAsync(byte[] buffer, int index, int count) {
            if (IsClosedOrErrorState) {
                throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
            }
            try {
                await AdvanceStateAsync(Token.Text).ConfigureAwait(false);
                await base.WriteBinHexAsync(buffer, index, count).ConfigureAwait(false);
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        private async Task WriteStartDocumentImplAsync(XmlStandalone standalone) {
            try {
                await AdvanceStateAsync(Token.StartDocument).ConfigureAwait(false);
 
                if (conformanceLevel == ConformanceLevel.Auto) {
                    conformanceLevel = ConformanceLevel.Document;
                    stateTable = StateTableDocument;
                }
                else if (conformanceLevel == ConformanceLevel.Fragment) {
                    throw new InvalidOperationException(Res.GetString(Res.Xml_CannotStartDocumentOnFragment));
                }
 
                if (rawWriter != null) {
                    if (!xmlDeclFollows) {
                        await rawWriter.WriteXmlDeclarationAsync(standalone).ConfigureAwait(false);
                    }
                }
                else {
                    // We do not pass the standalone value here - Dev10 Bug #479769
                    await writer.WriteStartDocumentAsync().ConfigureAwait(false);
                }
            }
            catch {
                currentState = State.Error;
                throw;
            }
        }
 
        //call taskFun and change state in sequence
        private Task AdvanceStateAsync_ReturnWhenFinish(Task task, State newState) {
            if (task.IsSuccess()) {
                currentState = newState;
                return AsyncHelper.DoneTask;
            }
            else {
                return _AdvanceStateAsync_ReturnWhenFinish(task, newState);
            }
        }
 
        private async Task _AdvanceStateAsync_ReturnWhenFinish(Task task, State newState) {
            await task.ConfigureAwait(false);
            currentState = newState;
        }
 
        private Task AdvanceStateAsync_ContinueWhenFinish(Task task, State newState, Token token) {
            if (task.IsSuccess()) {
                currentState = newState;
                return AdvanceStateAsync(token);
            }
            else {
                return _AdvanceStateAsync_ContinueWhenFinish(task, newState, token);
            }
        }
 
        private async Task _AdvanceStateAsync_ContinueWhenFinish(Task task, State newState, Token token) {
            await task.ConfigureAwait(false);
            currentState = newState;
            await AdvanceStateAsync(token).ConfigureAwait(false);
        }
 
        // Advance the state machine
        private Task AdvanceStateAsync(Token token) {
            if ((int)currentState >= (int)State.Closed) {
                if (currentState == State.Closed || currentState == State.Error) {
                    throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
                }
                else {
                    throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], GetStateName(currentState)));
                }
            }
       Advance:
            State newState = stateTable[((int)token << 4) + (int)currentState];
            //                         [ (int)token * 16 + (int)currentState ];
 
            Task task;
            if ((int)newState >= (int)State.Error) {
                switch (newState) {
                    case State.Error:
                        ThrowInvalidStateTransition(token, currentState);
                        break;
 
                    case State.StartContent:
                        return AdvanceStateAsync_ReturnWhenFinish(StartElementContentAsync(), State.Content);
 
                    case State.StartContentEle:
                        return AdvanceStateAsync_ReturnWhenFinish(StartElementContentAsync(), State.Element);
 
                    case State.StartContentB64:
                        return AdvanceStateAsync_ReturnWhenFinish(StartElementContentAsync(), State.B64Content);
 
                    case State.StartDoc:
                        return AdvanceStateAsync_ReturnWhenFinish(WriteStartDocumentAsync(), State.Document);
 
                    case State.StartDocEle:
                        return AdvanceStateAsync_ReturnWhenFinish(WriteStartDocumentAsync(), State.Element);
 
                    case State.EndAttrSEle:
                        task = SequenceRun(WriteEndAttributeAsync(), StartElementContentAsync);
                        return AdvanceStateAsync_ReturnWhenFinish(task, State.Element);
 
                    case State.EndAttrEEle:
                        task = SequenceRun(WriteEndAttributeAsync(), StartElementContentAsync);
                        return AdvanceStateAsync_ReturnWhenFinish(task, State.Content);
                    case State.EndAttrSCont:
                        task = SequenceRun(WriteEndAttributeAsync(), StartElementContentAsync);
                        return AdvanceStateAsync_ReturnWhenFinish(task, State.Content);
 
                    case State.EndAttrSAttr:
                        return AdvanceStateAsync_ReturnWhenFinish(WriteEndAttributeAsync(), State.Attribute);
 
                    case State.PostB64Cont:
                        if (rawWriter != null) {
                            return AdvanceStateAsync_ContinueWhenFinish(rawWriter.WriteEndBase64Async(), State.Content, token);
                        }
                        currentState = State.Content;
                        goto Advance;
 
                    case State.PostB64Attr:
                        if (rawWriter != null) {
                            return AdvanceStateAsync_ContinueWhenFinish(rawWriter.WriteEndBase64Async(), State.Attribute, token);
                        }
                        currentState = State.Attribute;
                        goto Advance;
 
                    case State.PostB64RootAttr:
                        if (rawWriter != null) {
                            return AdvanceStateAsync_ContinueWhenFinish(rawWriter.WriteEndBase64Async(), State.RootLevelAttr, token);
                        }
                        currentState = State.RootLevelAttr;
                        goto Advance;
 
                    case State.StartFragEle:
                        StartFragment();
                        newState = State.Element;
                        break;
 
                    case State.StartFragCont:
                        StartFragment();
                        newState = State.Content;
                        break;
 
                    case State.StartFragB64:
                        StartFragment();
                        newState = State.B64Content;
                        break;
 
                    case State.StartRootLevelAttr:
                        return AdvanceStateAsync_ReturnWhenFinish(WriteEndAttributeAsync(), State.RootLevelAttr);
 
 
                    default:
                        Debug.Assert(false, "We should not get to this point.");
                        break;
                }
            }
 
            currentState = newState;
            return AsyncHelper.DoneTask;
        }
 
        // write namespace declarations
        private async Task StartElementContentAsync_WithNS() { 
            int start = elemScopeStack[elemTop].prevNSTop;
            for (int i = nsTop; i > start; i--) {
                if (nsStack[i].kind == NamespaceKind.NeedToWrite) {
                    await nsStack[i].WriteDeclAsync(writer, rawWriter).ConfigureAwait(false);
                }
            }
            if (rawWriter != null) {
                rawWriter.StartElementContent();
            }
        }
 
        private Task StartElementContentAsync() {
            if (nsTop > elemScopeStack[elemTop].prevNSTop) {
                return StartElementContentAsync_WithNS();
            }
 
            if (rawWriter != null) {
                rawWriter.StartElementContent();
            }
            return AsyncHelper.DoneTask;
        }
    }
}