File: System\Xml\XmlDictionaryWriter.cs
Project: ndp\cdf\src\WCF\Serialization\System.Runtime.Serialization.csproj (System.Runtime.Serialization)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.Xml
{
    using System;
    using System.IO;
    using System.Xml;
    using System.Collections;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Security;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Text;
    using System.Runtime;
 
    public abstract class XmlDictionaryWriter : XmlWriter
    {
        // Derived classes that implements WriteBase64Async should override 
        // this flag to true so that the base XmlDictionaryWriter would use the 
        // faster WriteBase64Async APIs instead of the default BeginWrite() implementation.
        internal virtual bool FastAsync
        {
            get { return false; }
        }
 
        internal virtual AsyncCompletionResult WriteBase64Async(AsyncEventArgs<XmlWriteBase64AsyncArguments> state)
        {
            throw FxTrace.Exception.AsError(new NotSupportedException());
        }
 
        public override Task WriteBase64Async(byte[] buffer, int index, int count)
        {
            return Task.Factory.FromAsync(this.BeginWriteBase64, this.EndWriteBase64, buffer, index, count, null);
        }
 
        internal virtual IAsyncResult BeginWriteBase64(byte[] buffer, int index, int count, AsyncCallback callback, object state)
        {
            return new WriteBase64AsyncResult(buffer, index, count, this, callback, state);
        }
 
        internal virtual void EndWriteBase64(IAsyncResult result)
        {
            WriteBase64AsyncResult.End(result);
        }
 
        static public XmlDictionaryWriter CreateBinaryWriter(Stream stream)
        {
            return CreateBinaryWriter(stream, null);
        }
 
        static public XmlDictionaryWriter CreateBinaryWriter(Stream stream, IXmlDictionary dictionary)
        {
            return CreateBinaryWriter(stream, dictionary, null);
        }
 
        static public XmlDictionaryWriter CreateBinaryWriter(Stream stream, IXmlDictionary dictionary, XmlBinaryWriterSession session)
        {
            return CreateBinaryWriter(stream, dictionary, session, true);
        }
 
        static public XmlDictionaryWriter CreateBinaryWriter(Stream stream, IXmlDictionary dictionary, XmlBinaryWriterSession session, bool ownsStream)
        {
            XmlBinaryWriter writer = new XmlBinaryWriter();
            writer.SetOutput(stream, dictionary, session, ownsStream);
            return writer;
        }
 
        static public XmlDictionaryWriter CreateTextWriter(Stream stream)
        {
            return CreateTextWriter(stream, Encoding.UTF8, true);
        }
 
        static public XmlDictionaryWriter CreateTextWriter(Stream stream, Encoding encoding)
        {
            return CreateTextWriter(stream, encoding, true);
        }
 
        static public XmlDictionaryWriter CreateTextWriter(Stream stream, Encoding encoding, bool ownsStream)
        {
            XmlUTF8TextWriter writer = new XmlUTF8TextWriter();
            writer.SetOutput(stream, encoding, ownsStream);
            return writer;
        }
 
        static public XmlDictionaryWriter CreateMtomWriter(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo)
        {
            return CreateMtomWriter(stream, encoding, maxSizeInBytes, startInfo, null, null, true, true);
        }
 
        static public XmlDictionaryWriter CreateMtomWriter(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        {
            XmlMtomWriter writer = new XmlMtomWriter();
            writer.SetOutput(stream, encoding, maxSizeInBytes, startInfo, boundary, startUri, writeMessageHeaders, ownsStream);
            return writer;
        }
 
        static public XmlDictionaryWriter CreateDictionaryWriter(XmlWriter writer)
        {
            if (writer == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
 
            XmlDictionaryWriter dictionaryWriter = writer as XmlDictionaryWriter;
 
            if (dictionaryWriter == null)
            {
                dictionaryWriter = new XmlWrappedWriter(writer);
            }
 
            return dictionaryWriter;
        }
 
        public void WriteStartElement(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            WriteStartElement((string)null, localName, namespaceUri);
        }
 
        public virtual void WriteStartElement(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            WriteStartElement(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri));
        }
 
        public void WriteStartAttribute(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            WriteStartAttribute((string)null, localName, namespaceUri);
        }
 
        public virtual void WriteStartAttribute(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            WriteStartAttribute(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri));
        }
 
        public void WriteAttributeString(XmlDictionaryString localName, XmlDictionaryString namespaceUri, string value)
        {
            WriteAttributeString((string)null, localName, namespaceUri, value);
        }
 
        public virtual void WriteXmlnsAttribute(string prefix, string namespaceUri)
        {
            if (namespaceUri == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("namespaceUri");
            if (prefix == null)
            {
                if (LookupPrefix(namespaceUri) != null)
                    return;
#pragma warning suppress 56506 // Microsoft, namespaceUri is already checked
                prefix = namespaceUri.Length == 0 ? string.Empty : string.Concat("d", namespaceUri.Length.ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
            }
            WriteAttributeString("xmlns", prefix, null, namespaceUri);
        }
 
        public virtual void WriteXmlnsAttribute(string prefix, XmlDictionaryString namespaceUri)
        {
            WriteXmlnsAttribute(prefix, XmlDictionaryString.GetString(namespaceUri));
        }
 
        public virtual void WriteXmlAttribute(string localName, string value)
        {
            WriteAttributeString("xml", localName, null, value);
        }
 
        public virtual void WriteXmlAttribute(XmlDictionaryString localName, XmlDictionaryString value)
        {
            WriteXmlAttribute(XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(value));
        }
 
        public void WriteAttributeString(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, string value)
        {
            WriteStartAttribute(prefix, localName, namespaceUri);
            WriteString(value);
            WriteEndAttribute();
        }
 
        public void WriteElementString(XmlDictionaryString localName, XmlDictionaryString namespaceUri, string value)
        {
            WriteElementString((string)null, localName, namespaceUri, value);
        }
 
        public void WriteElementString(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, string value)
        {
            WriteStartElement(prefix, localName, namespaceUri);
            WriteString(value);
            WriteEndElement();
        }
 
        public virtual void WriteString(XmlDictionaryString value)
        {
            WriteString(XmlDictionaryString.GetString(value));
        }
 
        public virtual void WriteQualifiedName(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            if (localName == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("localName"));
            if (namespaceUri == null)
                namespaceUri = XmlDictionaryString.Empty;
#pragma warning suppress 56506 // Microsoft, XmlDictionaryString.Empty is never null
            WriteQualifiedName(localName.Value, namespaceUri.Value);
        }
 
        public virtual void WriteValue(XmlDictionaryString value)
        {
            WriteValue(XmlDictionaryString.GetString(value));
        }
 
        public virtual void WriteValue(IStreamProvider value)
        {
            if (value == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            Stream stream = value.GetStream();
            if (stream == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidStream)));
            int blockSize = 256;
            int bytesRead = 0;
            byte[] block = new byte[blockSize];
            while (true)
            {
                bytesRead = stream.Read(block, 0, blockSize);
                if (bytesRead > 0)
                    WriteBase64(block, 0, bytesRead);
                else
                    break;
                if (blockSize < 65536 && bytesRead == blockSize)
                {
                    blockSize = blockSize * 16;
                    block = new byte[blockSize];
                }
            }
            value.ReleaseStream(stream);
        }
 
        public virtual Task WriteValueAsync(IStreamProvider value)
        {
            return Task.Factory.FromAsync(this.BeginWriteValue, this.EndWriteValue, value, null);
        }
 
        internal virtual IAsyncResult BeginWriteValue(IStreamProvider value, AsyncCallback callback, object state)
        {
            if (value == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            if (this.FastAsync)
            {
                return new WriteValueFastAsyncResult(this, value, callback, state);
            }
            else
            {
                return new WriteValueAsyncResult(this, value, callback, state);
            }
        }
 
        internal virtual void EndWriteValue(IAsyncResult result)
        {
            if (this.FastAsync)
            {
                WriteValueFastAsyncResult.End(result);
            }
            else
            {
                WriteValueAsyncResult.End(result);
            }
        }
 
        class WriteValueFastAsyncResult : AsyncResult
        {
            bool completed;
            int blockSize;
            byte[] block;
            int bytesRead;
            Stream stream;
            Operation nextOperation;
            IStreamProvider streamProvider;
            XmlDictionaryWriter writer;
            AsyncEventArgs<XmlWriteBase64AsyncArguments> writerAsyncState;
            XmlWriteBase64AsyncArguments writerAsyncArgs;
 
            static AsyncCallback onReadComplete = Fx.ThunkCallback(OnReadComplete);
            static AsyncEventArgsCallback onWriteComplete;
 
            public WriteValueFastAsyncResult(XmlDictionaryWriter writer, IStreamProvider value, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.streamProvider = value;
                this.writer = writer;
                this.stream = value.GetStream();
                if (this.stream == null)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidStream)));
                }
                this.blockSize = 256;
                this.bytesRead = 0;
                this.block = new byte[this.blockSize];
                this.nextOperation = Operation.Read;
                this.ContinueWork(true);
            }
 
            void CompleteAndReleaseStream(bool completedSynchronously, Exception completionException = null)
            {
                if (completionException == null)
                {
                    // only release stream when no exception (mirrors sync behaviour)
                    this.streamProvider.ReleaseStream(this.stream);
                    this.stream = null;
                }
 
                this.Complete(completedSynchronously, completionException);
            }
 
            void ContinueWork(bool completedSynchronously, Exception completionException = null)
            {
                // Individual Reads or writes may complete sync or async. A callback however 
                // will always all ContinueWork() with CompletedSynchronously=false this flag 
                // is used to complete this AsyncResult.
                try
                {
                    while (true)
                    {
                        if (this.nextOperation == Operation.Read)
                        {
                            if (ReadAsync() != AsyncCompletionResult.Completed)
                            {
                                return;
                            }
                        }
                        else if (this.nextOperation == Operation.Write)
                        {
                            if (WriteAsync() != AsyncCompletionResult.Completed)
                            {
                                return;
                            }
                        }
                        else if (this.nextOperation == Operation.Complete)
                        {
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
 
                    if (completedSynchronously)
                    {
                        throw;
                    }
 
                    if (completionException == null)
                    {
                        completionException = ex;
                    }
                }
 
                if (!completed)
                {
                    this.completed = true;
                    this.CompleteAndReleaseStream(completedSynchronously, completionException);
                }
            }
 
            AsyncCompletionResult ReadAsync()
            {
                IAsyncResult result = this.stream.BeginRead(this.block, 0, blockSize, onReadComplete, this);
                if (result.CompletedSynchronously)
                {
                    this.HandleReadComplete(result);
                    return AsyncCompletionResult.Completed;
                }
 
                return AsyncCompletionResult.Queued;
            }
 
            void HandleReadComplete(IAsyncResult result)
            {
                this.bytesRead = this.stream.EndRead(result);
                if (this.bytesRead > 0)
                {
                    this.nextOperation = Operation.Write;
                }
                else
                {
                    this.nextOperation = Operation.Complete;
                }
            }
 
            static void OnReadComplete(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                {
                    return;
                }
 
                Exception completionException = null;
                WriteValueFastAsyncResult thisPtr = (WriteValueFastAsyncResult)result.AsyncState;
                bool sucess = false;
                try
                {
                    thisPtr.HandleReadComplete(result);
                    sucess = true;
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
 
                    completionException = ex;
                }
 
                if (!sucess)
                {
                    thisPtr.nextOperation = Operation.Complete;
                }
 
                thisPtr.ContinueWork(false, completionException);
            }
 
            AsyncCompletionResult WriteAsync()
            {
                if (this.writerAsyncState == null)
                {
                    this.writerAsyncArgs = new XmlWriteBase64AsyncArguments();
                    this.writerAsyncState = new AsyncEventArgs<XmlWriteBase64AsyncArguments>();
                }
 
                if (onWriteComplete == null)
                {
                    onWriteComplete = new AsyncEventArgsCallback(OnWriteComplete);
                }
 
                this.writerAsyncArgs.Buffer = this.block;
                this.writerAsyncArgs.Offset = 0;
                this.writerAsyncArgs.Count = this.bytesRead;
 
                this.writerAsyncState.Set(onWriteComplete, this.writerAsyncArgs, this);
                if (this.writer.WriteBase64Async(this.writerAsyncState) == AsyncCompletionResult.Completed)
                {
                    this.HandleWriteComplete();
                    this.writerAsyncState.Complete(true);
                    return AsyncCompletionResult.Completed;
                }
 
                return AsyncCompletionResult.Queued;
            }
 
            void HandleWriteComplete()
            {
                this.nextOperation = Operation.Read;
                // Adjust block size for next read
                if (this.blockSize < 65536 && this.bytesRead == this.blockSize)
                {
                    this.blockSize = this.blockSize * 16;
                    this.block = new byte[this.blockSize];
                }
            }
 
            static void OnWriteComplete(IAsyncEventArgs asyncState)
            {
                WriteValueFastAsyncResult thisPtr = (WriteValueFastAsyncResult)asyncState.AsyncState;
                Exception completionException = null;
                bool success = false;
                try
                {
                    if (asyncState.Exception != null)
                    {
                        completionException = asyncState.Exception;
                    }
                    else
                    {
                        thisPtr.HandleWriteComplete();
                        success = true;
                    }
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
 
                    completionException = ex;
                }
 
                if (!success)
                {
                    thisPtr.nextOperation = Operation.Complete;
                }
 
                thisPtr.ContinueWork(false, completionException);
            }
 
            internal static void End(IAsyncResult result)
            {
                AsyncResult.End<WriteValueFastAsyncResult>(result);
            }
 
            enum Operation
            {
                Read,
                Write,
                Complete
            }
        }
 
        class WriteValueAsyncResult : AsyncResult
        {
            enum Operation
            {
                Read,
                Write
            }
 
            int blockSize;
            byte[] block;
            int bytesRead;
            Stream stream;
            Operation operation = Operation.Read;
            IStreamProvider streamProvider;
            XmlDictionaryWriter writer;
            Func<IAsyncResult, WriteValueAsyncResult, bool> writeBlockHandler;
 
            static Func<IAsyncResult, WriteValueAsyncResult, bool> handleWriteBlock = HandleWriteBlock;
            static Func<IAsyncResult, WriteValueAsyncResult, bool> handleWriteBlockAsync = HandleWriteBlockAsync;
            static AsyncCallback onContinueWork = Fx.ThunkCallback(OnContinueWork);
 
            public WriteValueAsyncResult(XmlDictionaryWriter writer, IStreamProvider value, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.streamProvider = value;
                this.writer = writer;
                this.writeBlockHandler = this.writer.Settings != null && this.writer.Settings.Async ? handleWriteBlockAsync : handleWriteBlock;
                this.stream = value.GetStream();
                if (this.stream == null)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidStream)));
                }
                this.blockSize = 256;
                this.bytesRead = 0;
                this.block = new byte[blockSize];     // 
 
                bool completeSelf = ContinueWork(null);
 
                if (completeSelf)
                {
                    this.CompleteAndReleaseStream(true, null);
                }
            }
 
            void AdjustBlockSize()
            {
                if (this.blockSize < 65536 && this.bytesRead == this.blockSize)
                {
                    this.blockSize = this.blockSize * 16;
                    this.block = new byte[this.blockSize];
                }
            }
 
            void CompleteAndReleaseStream(bool completedSynchronously, Exception completionException)
            {
                if (completionException == null)
                {
                    // only release stream when no exception (mirrors sync behaviour)
                    this.streamProvider.ReleaseStream(this.stream);
                    this.stream = null;
                }
 
                this.Complete(completedSynchronously, completionException);
            }
 
            //returns whether or not the entire operation is completed
            bool ContinueWork(IAsyncResult result)
            {
                while (true)
                {
                    if (this.operation == Operation.Read)
                    {
                        if (HandleReadBlock(result))
                        {
                            // Read completed (sync or async, doesn't matter) 
                            if (this.bytesRead > 0)
                            {
                                // allow loop to continue at Write
                                operation = Operation.Write;
                            }
                            else
                            {
                                // Exit loop, entire source stream has been read.  
                                return true;
                            }
                        }
                        else
                        {
                            // Read completed async, jump out of loop, callback resumes at writing.
                            return false;
                        }
                    }
                    else
                    {
                        if (this.writeBlockHandler(result, this))
                        {
                            // Write completed (sync or async, doesn't matter) 
                            AdjustBlockSize();
                            operation = Operation.Read;
                        }
                        else
                        {
                            // Write completed async, jump out of loop, callback should resume at reading
                            return false;
                        }
                    }
                    result = null;
                }
            }
 
            //returns whether or not I completed.
            bool HandleReadBlock(IAsyncResult result)
            {
                if (result == null)
                {
                    result = this.stream.BeginRead(this.block, 0, blockSize, onContinueWork, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                }
 
                this.bytesRead = this.stream.EndRead(result);
                return true;
            }
 
            //returns whether or not I completed.
            static bool HandleWriteBlock(IAsyncResult result, WriteValueAsyncResult thisPtr)
            {
                if (result == null)
                {
                    result = thisPtr.writer.BeginWriteBase64(thisPtr.block, 0, thisPtr.bytesRead, onContinueWork, thisPtr);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                }
 
                thisPtr.writer.EndWriteBase64(result);
                return true;
            }
 
            //returns whether or not I completed.
            static bool HandleWriteBlockAsync(IAsyncResult result, WriteValueAsyncResult thisPtr)
            {
                Task task = (Task)result;
 
                if (task == null)
                {
                    task = thisPtr.writer.WriteBase64Async(thisPtr.block, 0, thisPtr.bytesRead);
                    task.AsAsyncResult(onContinueWork, thisPtr);
                    return false;
                }
 
                task.GetAwaiter().GetResult();
 
                return true;
            }
 
            static void OnContinueWork(IAsyncResult result)
            {
                // If result is a Task we shouldn't check for Synchronous completion 
                // We should only return if we're in the async completion path and if the result completed synchronously.
                if (result.CompletedSynchronously && !(result is Task))
                {
                    return;
                }
 
                Exception completionException = null;
                WriteValueAsyncResult thisPtr = (WriteValueAsyncResult)result.AsyncState;
                bool completeSelf = false;
 
                try
                {
                    completeSelf = thisPtr.ContinueWork(result);
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
                    completeSelf = true;
                    completionException = ex;
                }
 
                if (completeSelf)
                {
                    thisPtr.CompleteAndReleaseStream(false, completionException);
                }
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<WriteValueAsyncResult>(result);
            }
        }
 
        public virtual void WriteValue(UniqueId value)
        {
            if (value == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
 
            WriteString(value.ToString());
        }
 
        public virtual void WriteValue(Guid value)
        {
            WriteString(value.ToString());
        }
 
        public virtual void WriteValue(TimeSpan value)
        {
            WriteString(XmlConvert.ToString(value));
        }
 
        public virtual bool CanCanonicalize
        {
            get
            {
                return false;
            }
        }
 
        public virtual void StartCanonicalization(Stream stream, bool includeComments, string[] inclusivePrefixes)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }
 
        public virtual void EndCanonicalization()
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }
 
        void WriteElementNode(XmlDictionaryReader reader, bool defattr)
        {
            XmlDictionaryString localName;
            XmlDictionaryString namespaceUri;
            if (reader.TryGetLocalNameAsDictionaryString(out localName) && reader.TryGetNamespaceUriAsDictionaryString(out namespaceUri))
            {
                WriteStartElement(reader.Prefix, localName, namespaceUri);
            }
            else
            {
                WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
            }
            if (defattr || (!reader.IsDefault && (reader.SchemaInfo == null || !reader.SchemaInfo.IsDefault)))
            {
                if (reader.MoveToFirstAttribute())
                {
                    do
                    {
                        if (reader.TryGetLocalNameAsDictionaryString(out localName) && reader.TryGetNamespaceUriAsDictionaryString(out namespaceUri))
                        {
                            WriteStartAttribute(reader.Prefix, localName, namespaceUri);
                        }
                        else
                        {
                            WriteStartAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                        }
                        while (reader.ReadAttributeValue())
                        {
                            if (reader.NodeType == XmlNodeType.EntityReference)
                            {
                                WriteEntityRef(reader.Name);
                            }
                            else
                            {
                                WriteTextNode(reader, true);
                            }
                        }
                        WriteEndAttribute();
                    }
                    while (reader.MoveToNextAttribute());
                    reader.MoveToElement();
                }
            }
            if (reader.IsEmptyElement)
            {
                WriteEndElement();
            }
        }
 
        void WriteArrayNode(XmlDictionaryReader reader, string prefix, string localName, string namespaceUri, Type type)
        {
            if (type == typeof(bool))
                BooleanArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int16))
                Int16ArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int32))
                Int32ArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int64))
                Int64ArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(float))
                SingleArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(double))
                DoubleArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(decimal))
                DecimalArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(DateTime))
                DateTimeArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Guid))
                GuidArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(TimeSpan))
                TimeSpanArrayHelperWithString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else
            {
                WriteElementNode(reader, false);
                reader.Read();
            }
        }
 
        void WriteArrayNode(XmlDictionaryReader reader, string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Type type)
        {
            if (type == typeof(bool))
                BooleanArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int16))
                Int16ArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int32))
                Int32ArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Int64))
                Int64ArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(float))
                SingleArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(double))
                DoubleArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(decimal))
                DecimalArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(DateTime))
                DateTimeArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(Guid))
                GuidArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else if (type == typeof(TimeSpan))
                TimeSpanArrayHelperWithDictionaryString.Instance.WriteArray(this, prefix, localName, namespaceUri, reader);
            else
            {
                WriteElementNode(reader, false);
                reader.Read();
            }
        }
 
        void WriteArrayNode(XmlDictionaryReader reader, Type type)
        {
            XmlDictionaryString localName;
            XmlDictionaryString namespaceUri;
            if (reader.TryGetLocalNameAsDictionaryString(out localName) && reader.TryGetNamespaceUriAsDictionaryString(out namespaceUri))
                WriteArrayNode(reader, reader.Prefix, localName, namespaceUri, type);
            else
                WriteArrayNode(reader, reader.Prefix, reader.LocalName, reader.NamespaceURI, type);
        }
 
        protected virtual void WriteTextNode(XmlDictionaryReader reader, bool isAttribute)
        {
            XmlDictionaryString value;
            if (reader.TryGetValueAsDictionaryString(out value))
            {
                WriteString(value);
            }
            else
            {
                WriteString(reader.Value);
            }
            if (!isAttribute)
            {
                reader.Read();
            }
        }
 
        public override void WriteNode(XmlReader reader, bool defattr)
        {
            XmlDictionaryReader dictionaryReader = reader as XmlDictionaryReader;
            if (dictionaryReader != null)
                WriteNode(dictionaryReader, defattr);
            else
                base.WriteNode(reader, defattr);
        }
 
        public virtual void WriteNode(XmlDictionaryReader reader, bool defattr)
        {
            if (reader == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader"));
            int d = (reader.NodeType == XmlNodeType.None ? -1 : reader.Depth);
            do
            {
                XmlNodeType nodeType = reader.NodeType;
                Type type;
                if (nodeType == XmlNodeType.Text || nodeType == XmlNodeType.Whitespace || nodeType == XmlNodeType.SignificantWhitespace)
                {
                    // This will advance if necessary, so we don't need to call Read() explicitly
                    WriteTextNode(reader, false);
                }
                else if (reader.Depth > d && reader.IsStartArray(out type))
                {
                    WriteArrayNode(reader, type);
                }
                else
                {
                    // These will not advance, so we must call Read() explicitly
                    switch (nodeType)
                    {
                        case XmlNodeType.Element:
                            WriteElementNode(reader, defattr);
                            break;
                        case XmlNodeType.CDATA:
                            WriteCData(reader.Value);
                            break;
                        case XmlNodeType.EntityReference:
                            WriteEntityRef(reader.Name);
                            break;
                        case XmlNodeType.XmlDeclaration:
                        case XmlNodeType.ProcessingInstruction:
                            WriteProcessingInstruction(reader.Name, reader.Value);
                            break;
                        case XmlNodeType.DocumentType:
                            WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
                            break;
                        case XmlNodeType.Comment:
                            WriteComment(reader.Value);
                            break;
                        case XmlNodeType.EndElement:
                            WriteFullEndElement();
                            break;
                    }
                    if (!reader.Read())
                        break;
                }
            }
            while (d < reader.Depth || (d == reader.Depth && reader.NodeType == XmlNodeType.EndElement));
        }
 
        void CheckArray(Array array, int offset, int count)
        {
            if (array == null)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("array"));
            if (offset < 0)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
            if (offset > array.Length)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, array.Length)));
            if (count < 0)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
            if (count > array.Length - offset)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, array.Length - offset)));
        }
 
        // bool
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, bool[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, bool[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // Int16
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, Int16[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Int16[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // Int32
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, Int32[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Int32[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // Int64
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, Int64[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Int64[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // float
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, float[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, float[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // double
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, double[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, double[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // decimal
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, decimal[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, decimal[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // DateTime
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, DateTime[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, DateTime[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // Guid
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, Guid[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Guid[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        // TimeSpan
        public virtual void WriteArray(string prefix, string localName, string namespaceUri, TimeSpan[] array, int offset, int count)
        {
            CheckArray(array, offset, count);
            for (int i = 0; i < count; i++)
            {
                WriteStartElement(prefix, localName, namespaceUri);
                WriteValue(array[offset + i]);
                WriteEndElement();
            }
        }
 
        public virtual void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, TimeSpan[] array, int offset, int count)
        {
            WriteArray(prefix, XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count);
        }
 
        class WriteBase64AsyncResult : ScheduleActionItemAsyncResult
        {
            byte[] buffer;
            int index;
            int count;
            XmlDictionaryWriter writer;
 
            public WriteBase64AsyncResult(byte[] buffer, int index, int count, XmlDictionaryWriter writer, AsyncCallback callback, object state)
                : base(callback, state)
            {
                Fx.Assert(writer != null, "writer should never be null");
 
                this.buffer = buffer;
                this.index = index;
                this.count = count;
                this.writer = writer;
 
                Schedule();
            }
 
            protected override void OnDoWork()
            {
                this.writer.WriteBase64(this.buffer, this.index, this.count);
            }
        }
 
        class XmlWrappedWriter : XmlDictionaryWriter
        {
            XmlWriter writer;
            int depth;
            int prefix;
 
            public XmlWrappedWriter(XmlWriter writer)
            {
                this.writer = writer;
                this.depth = 0;
            }
 
            public override void Close()
            {
                writer.Close();
            }
 
            public override void Flush()
            {
                writer.Flush();
            }
 
            public override string LookupPrefix(string namespaceUri)
            {
                return writer.LookupPrefix(namespaceUri);
            }
 
            public override void WriteAttributes(XmlReader reader, bool defattr)
            {
                writer.WriteAttributes(reader, defattr);
            }
 
            public override void WriteBase64(byte[] buffer, int index, int count)
            {
                writer.WriteBase64(buffer, index, count);
            }
 
            public override void WriteBinHex(byte[] buffer, int index, int count)
            {
                writer.WriteBinHex(buffer, index, count);
            }
 
            public override void WriteCData(string text)
            {
                writer.WriteCData(text);
            }
 
            public override void WriteCharEntity(char ch)
            {
                writer.WriteCharEntity(ch);
            }
 
            public override void WriteChars(char[] buffer, int index, int count)
            {
                writer.WriteChars(buffer, index, count);
            }
 
            public override void WriteComment(string text)
            {
                writer.WriteComment(text);
            }
 
            public override void WriteDocType(string name, string pubid, string sysid, string subset)
            {
                writer.WriteDocType(name, pubid, sysid, subset);
            }
 
            public override void WriteEndAttribute()
            {
                writer.WriteEndAttribute();
            }
 
            public override void WriteEndDocument()
            {
                writer.WriteEndDocument();
            }
 
            public override void WriteEndElement()
            {
                writer.WriteEndElement();
                this.depth--;
            }
 
            public override void WriteEntityRef(string name)
            {
                writer.WriteEntityRef(name);
            }
 
            public override void WriteFullEndElement()
            {
                writer.WriteFullEndElement();
            }
 
            public override void WriteName(string name)
            {
                writer.WriteName(name);
            }
 
            public override void WriteNmToken(string name)
            {
                writer.WriteNmToken(name);
            }
 
            public override void WriteNode(XmlReader reader, bool defattr)
            {
                writer.WriteNode(reader, defattr);
            }
 
            public override void WriteProcessingInstruction(string name, string text)
            {
                writer.WriteProcessingInstruction(name, text);
            }
 
            public override void WriteQualifiedName(string localName, string namespaceUri)
            {
                writer.WriteQualifiedName(localName, namespaceUri);
            }
 
            public override void WriteRaw(char[] buffer, int index, int count)
            {
                writer.WriteRaw(buffer, index, count);
            }
 
            public override void WriteRaw(string data)
            {
                writer.WriteRaw(data);
            }
 
            public override void WriteStartAttribute(string prefix, string localName, string namespaceUri)
            {
                writer.WriteStartAttribute(prefix, localName, namespaceUri);
                this.prefix++;
            }
 
            public override void WriteStartDocument()
            {
                writer.WriteStartDocument();
            }
 
            public override void WriteStartDocument(bool standalone)
            {
                writer.WriteStartDocument(standalone);
            }
 
            public override void WriteStartElement(string prefix, string localName, string namespaceUri)
            {
                writer.WriteStartElement(prefix, localName, namespaceUri);
                this.depth++;
                this.prefix = 1;
            }
 
            public override WriteState WriteState
            {
                get
                {
                    return writer.WriteState;
                }
            }
 
            public override void WriteString(string text)
            {
                writer.WriteString(text);
            }
 
            public override void WriteSurrogateCharEntity(char lowChar, char highChar)
            {
                writer.WriteSurrogateCharEntity(lowChar, highChar);
            }
 
            public override void WriteWhitespace(string whitespace)
            {
                writer.WriteWhitespace(whitespace);
            }
 
            public override void WriteValue(object value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(string value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(bool value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(DateTime value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(double value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(int value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(long value)
            {
                writer.WriteValue(value);
            }
 
#if DECIMAL_FLOAT_API
            public override void WriteValue(decimal value)
            {
                writer.WriteValue(value);
            }
 
            public override void WriteValue(float value)
            {
                writer.WriteValue(value);
            }
#endif
            public override void WriteXmlnsAttribute(string prefix, string namespaceUri)
            {
                if (namespaceUri == null)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("namespaceUri");
                if (prefix == null)
                {
                    if (LookupPrefix(namespaceUri) != null)
                        return;
 
                    if (namespaceUri.Length == 0)
                    {
                        prefix = string.Empty;
                    }
                    else
                    {
                        string depthStr = this.depth.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
                        string prefixStr = this.prefix.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
                        prefix = string.Concat("d", depthStr, "p", prefixStr);
                    }
                }
                WriteAttributeString("xmlns", prefix, null, namespaceUri);
            }
 
            public override string XmlLang
            {
                get
                {
                    return writer.XmlLang;
                }
            }
 
            public override XmlSpace XmlSpace
            {
                get
                {
                    return writer.XmlSpace;
                }
            }
        }
    }
}