File: FrameworkFork\Microsoft.Xml\Xml\Core\XmlTextEncoder.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Globalization;
 
namespace Microsoft.Xml
{
    using System;
 
 
    // XmlTextEncoder
    //
    // This class does special handling of text content for XML.  For example
    // it will replace special characters with entities whenever necessary.
    internal class XmlTextEncoder
    {
        //
        // Fields
        //
        // output text writer
        private TextWriter _textWriter;
 
        // true when writing out the content of attribute value
        private bool _inAttribute;
 
        // quote char of the attribute (when inAttribute) 
        private char _quoteChar;
 
        // caching of attribute value
        private StringBuilder _attrValue;
        private bool _cacheAttrValue;
 
        // XmlCharType
        private XmlCharType _xmlCharType;
 
        //
        // Constructor
        //
        internal XmlTextEncoder(TextWriter textWriter)
        {
            _textWriter = textWriter;
            _quoteChar = '"';
            _xmlCharType = XmlCharType.Instance;
        }
 
        //
        // Internal methods and properties
        //
        internal char QuoteChar
        {
            set
            {
                _quoteChar = value;
            }
        }
 
        internal void StartAttribute(bool cacheAttrValue)
        {
            _inAttribute = true;
            _cacheAttrValue = cacheAttrValue;
            if (cacheAttrValue)
            {
                if (_attrValue == null)
                {
                    _attrValue = new StringBuilder();
                }
                else
                {
                    _attrValue.Length = 0;
                }
            }
        }
 
        internal void EndAttribute()
        {
            if (_cacheAttrValue)
            {
                _attrValue.Length = 0;
            }
            _inAttribute = false;
            _cacheAttrValue = false;
        }
 
        internal string AttributeValue
        {
            get
            {
                if (_cacheAttrValue)
                {
                    return _attrValue.ToString();
                }
                else
                {
                    return String.Empty;
                }
            }
        }
 
        internal void WriteSurrogateChar(char lowChar, char highChar)
        {
            if (!XmlCharType.IsLowSurrogate(lowChar) ||
                 !XmlCharType.IsHighSurrogate(highChar))
            {
                throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
            }
 
            _textWriter.Write(highChar);
            _textWriter.Write(lowChar);
        }
 
#if FEATURE_NETCORE
        [System.Security.SecurityCritical]
#endif
        internal void Write(char[] array, int offset, int count)
        {
            if (null == array)
            {
                throw new ArgumentNullException("array");
            }
 
            if (0 > offset)
            {
                throw new ArgumentOutOfRangeException("offset");
            }
 
            if (0 > count)
            {
                throw new ArgumentOutOfRangeException("count");
            }
 
            if (count > array.Length - offset)
            {
                throw new ArgumentOutOfRangeException("count");
            }
 
            if (_cacheAttrValue)
            {
                _attrValue.Append(array, offset, count);
            }
 
            int endPos = offset + count;
            int i = offset;
            char ch = (char)0;
            for (; ; )
            {
                int startPos = i;
                unsafe
                {
                    while (i < endPos && (_xmlCharType.charProperties[ch = array[i]] & XmlCharType.fAttrValue) != 0)
                    { // ( xmlCharType.IsAttributeValueChar( ( ch = array[i] ) ) ) ) {
                        i++;
                    }
                }
 
                if (startPos < i)
                {
                    _textWriter.Write(array, startPos, i - startPos);
                }
                if (i == endPos)
                {
                    break;
                }
 
                switch (ch)
                {
                    case (char)0x9:
                        _textWriter.Write(ch);
                        break;
                    case (char)0xA:
                    case (char)0xD:
                        if (_inAttribute)
                        {
                            WriteCharEntityImpl(ch);
                        }
                        else
                        {
                            _textWriter.Write(ch);
                        }
                        break;
 
                    case '<':
                        WriteEntityRefImpl("lt");
                        break;
                    case '>':
                        WriteEntityRefImpl("gt");
                        break;
                    case '&':
                        WriteEntityRefImpl("amp");
                        break;
                    case '\'':
                        if (_inAttribute && _quoteChar == ch)
                        {
                            WriteEntityRefImpl("apos");
                        }
                        else
                        {
                            _textWriter.Write('\'');
                        }
                        break;
                    case '"':
                        if (_inAttribute && _quoteChar == ch)
                        {
                            WriteEntityRefImpl("quot");
                        }
                        else
                        {
                            _textWriter.Write('"');
                        }
                        break;
                    default:
                        if (XmlCharType.IsHighSurrogate(ch))
                        {
                            if (i + 1 < endPos)
                            {
                                WriteSurrogateChar(array[++i], ch);
                            }
                            else
                            {
                                throw new ArgumentException(ResXml.Xml_SurrogatePairSplit);
                            }
                        }
                        else if (XmlCharType.IsLowSurrogate(ch))
                        {
                            throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
                        }
                        else
                        {
                            Debug.Assert((ch < 0x20 && !_xmlCharType.IsWhiteSpace(ch)) || (ch > 0xFFFD));
                            WriteCharEntityImpl(ch);
                        }
                        break;
                }
                i++;
            }
        }
 
        internal void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            if (!XmlCharType.IsLowSurrogate(lowChar) ||
                 !XmlCharType.IsHighSurrogate(highChar))
            {
                throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
            }
            int surrogateChar = XmlCharType.CombineSurrogateChar(lowChar, highChar);
 
            if (_cacheAttrValue)
            {
                _attrValue.Append(highChar);
                _attrValue.Append(lowChar);
            }
 
            _textWriter.Write("&#x");
            _textWriter.Write(surrogateChar.ToString("X", NumberFormatInfo.InvariantInfo));
            _textWriter.Write(';');
        }
 
#if FEATURE_NETCORE
        [System.Security.SecurityCritical]
#endif
        internal void Write(string text)
        {
            if (text == null)
            {
                return;
            }
 
            if (_cacheAttrValue)
            {
                _attrValue.Append(text);
            }
 
            // scan through the string to see if there are any characters to be escaped
            int len = text.Length;
            int i = 0;
            int startPos = 0;
            char ch = (char)0;
            for (; ; )
            {
                unsafe
                {
                    while (i < len && (_xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue) != 0)
                    { // ( xmlCharType.IsAttributeValueChar( ( ch = text[i] ) ) ) ) {
                        i++;
                    }
                }
                if (i == len)
                {
                    // reached the end of the string -> write it whole out
                    _textWriter.Write(text);
                    return;
                }
                if (_inAttribute)
                {
                    if (ch == 0x9)
                    {
                        i++;
                        continue;
                    }
                }
                else
                {
                    if (ch == 0x9 || ch == 0xA || ch == 0xD || ch == '"' || ch == '\'')
                    {
                        i++;
                        continue;
                    }
                }
                // some character that needs to be escaped is found:
                break;
            }
 
            char[] helperBuffer = new char[256];
            for (; ; )
            {
                if (startPos < i)
                {
                    WriteStringFragment(text, startPos, i - startPos, helperBuffer);
                }
                if (i == len)
                {
                    break;
                }
 
                switch (ch)
                {
                    case (char)0x9:
                        _textWriter.Write(ch);
                        break;
                    case (char)0xA:
                    case (char)0xD:
                        if (_inAttribute)
                        {
                            WriteCharEntityImpl(ch);
                        }
                        else
                        {
                            _textWriter.Write(ch);
                        }
                        break;
                    case '<':
                        WriteEntityRefImpl("lt");
                        break;
                    case '>':
                        WriteEntityRefImpl("gt");
                        break;
                    case '&':
                        WriteEntityRefImpl("amp");
                        break;
                    case '\'':
                        if (_inAttribute && _quoteChar == ch)
                        {
                            WriteEntityRefImpl("apos");
                        }
                        else
                        {
                            _textWriter.Write('\'');
                        }
                        break;
                    case '"':
                        if (_inAttribute && _quoteChar == ch)
                        {
                            WriteEntityRefImpl("quot");
                        }
                        else
                        {
                            _textWriter.Write('"');
                        }
                        break;
                    default:
                        if (XmlCharType.IsHighSurrogate(ch))
                        {
                            if (i + 1 < len)
                            {
                                WriteSurrogateChar(text[++i], ch);
                            }
                            else
                            {
                                throw XmlConvert.CreateInvalidSurrogatePairException(text[i], ch);
                            }
                        }
                        else if (XmlCharType.IsLowSurrogate(ch))
                        {
                            throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
                        }
                        else
                        {
                            Debug.Assert((ch < 0x20 && !_xmlCharType.IsWhiteSpace(ch)) || (ch > 0xFFFD));
                            WriteCharEntityImpl(ch);
                        }
                        break;
                }
                i++;
                startPos = i;
                unsafe
                {
                    while (i < len && (_xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue) != 0)
                    { // ( xmlCharType.IsAttributeValueChar( ( text[i] ) ) ) ) {
                        i++;
                    }
                }
            }
        }
 
#if FEATURE_NETCORE
        [System.Security.SecurityCritical]
#endif
        internal void WriteRawWithSurrogateChecking(string text)
        {
            if (text == null)
            {
                return;
            }
            if (_cacheAttrValue)
            {
                _attrValue.Append(text);
            }
 
            int len = text.Length;
            int i = 0;
            char ch = (char)0;
 
            for (; ; )
            {
                unsafe
                {
                    while (i < len &&
                        ((_xmlCharType.charProperties[ch = text[i]] & XmlCharType.fCharData) != 0 // ( xmlCharType.IsCharData( ( ch = text[i] ) ) 
                        || ch < 0x20))
                    {
                        i++;
                    }
                }
                if (i == len)
                {
                    break;
                }
                if (XmlCharType.IsHighSurrogate(ch))
                {
                    if (i + 1 < len)
                    {
                        char lowChar = text[i + 1];
                        if (XmlCharType.IsLowSurrogate(lowChar))
                        {
                            i += 2;
                            continue;
                        }
                        else
                        {
                            throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, ch);
                        }
                    }
                    throw new ArgumentException(ResXml.Xml_InvalidSurrogateMissingLowChar);
                }
                else if (XmlCharType.IsLowSurrogate(ch))
                {
                    throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
                }
                else
                {
                    i++;
                }
            }
 
            _textWriter.Write(text);
            return;
        }
 
        internal void WriteRaw(string value)
        {
            if (_cacheAttrValue)
            {
                _attrValue.Append(value);
            }
            _textWriter.Write(value);
        }
 
        internal void WriteRaw(char[] array, int offset, int count)
        {
            if (null == array)
            {
                throw new ArgumentNullException("array");
            }
 
            if (0 > count)
            {
                throw new ArgumentOutOfRangeException("count");
            }
 
            if (0 > offset)
            {
                throw new ArgumentOutOfRangeException("offset");
            }
 
            if (count > array.Length - offset)
            {
                throw new ArgumentOutOfRangeException("count");
            }
 
            if (_cacheAttrValue)
            {
                _attrValue.Append(array, offset, count);
            }
            _textWriter.Write(array, offset, count);
        }
 
 
 
        internal void WriteCharEntity(char ch)
        {
            if (XmlCharType.IsSurrogate(ch))
            {
                throw new ArgumentException(ResXml.Xml_InvalidSurrogateMissingLowChar);
            }
 
            string strVal = ((int)ch).ToString("X", NumberFormatInfo.InvariantInfo);
            if (_cacheAttrValue)
            {
                _attrValue.Append("&#x");
                _attrValue.Append(strVal);
                _attrValue.Append(';');
            }
            WriteCharEntityImpl(strVal);
        }
 
        internal void WriteEntityRef(string name)
        {
            if (_cacheAttrValue)
            {
                _attrValue.Append('&');
                _attrValue.Append(name);
                _attrValue.Append(';');
            }
            WriteEntityRefImpl(name);
        }
 
        internal void Flush()
        {
            // TODO?
        }
 
        //
        // Private implementation methods
        //
        // This is a helper method to woraround the fact that TextWriter does not have a Write method 
        // for fragment of a string such as Write( string, offset, count). 
        // The string fragment will be written out by copying into a small helper buffer and then 
        // calling textWriter to write out the buffer.
        private void WriteStringFragment(string str, int offset, int count, char[] helperBuffer)
        {
            int bufferSize = helperBuffer.Length;
            while (count > 0)
            {
                int copyCount = count;
                if (copyCount > bufferSize)
                {
                    copyCount = bufferSize;
                }
 
                str.CopyTo(offset, helperBuffer, 0, copyCount);
                _textWriter.Write(helperBuffer, 0, copyCount);
                offset += copyCount;
                count -= copyCount;
            }
        }
 
        private void WriteCharEntityImpl(char ch)
        {
            WriteCharEntityImpl(((int)ch).ToString("X", NumberFormatInfo.InvariantInfo));
        }
 
        private void WriteCharEntityImpl(string strVal)
        {
            _textWriter.Write("&#x");
            _textWriter.Write(strVal);
            _textWriter.Write(';');
        }
 
        private void WriteEntityRefImpl(string name)
        {
            _textWriter.Write('&');
            _textWriter.Write(name);
            _textWriter.Write(';');
        }
    }
}