The Labs \ Source Viewer \ SSCLI \ System.Xml \ XmlTextEncoder

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlTextWriter.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.IO;
  17. using System.Diagnostics;
  18. using System.Globalization;
  19. namespace System.Xml
  20. {
  21.    
  22.     // XmlTextEncoder
  23.     //
  24.     // This class does special handling of text content for XML. For example
  25.     // it will replace special characters with entities whenever necessary.
  26.     internal class XmlTextEncoder
  27.     {
  28.         //
  29.         // Fields
  30.         //
  31.         // output text writer
  32.         TextWriter textWriter;
  33.        
  34.         // true when writing out the content of attribute value
  35.         bool inAttribute;
  36.        
  37.         // quote char of the attribute (when inAttribute)
  38.         char quoteChar;
  39.        
  40.         // caching of attribute value
  41.         BufferBuilder attrValue;
  42.         bool cacheAttrValue;
  43.        
  44.         // XmlCharType
  45.         XmlCharType xmlCharType;
  46.        
  47.         //
  48.         // Constants
  49.         //
  50.         const int SurHighStart = 55296;
  51.         const int SurHighEnd = 56319;
  52.         const int SurLowStart = 56320;
  53.         const int SurLowEnd = 57343;
  54.        
  55.         //
  56.         // Constructor
  57.         //
  58.         internal XmlTextEncoder(TextWriter textWriter)
  59.         {
  60.             this.textWriter = textWriter;
  61.             this.quoteChar = '"';
  62.             this.xmlCharType = XmlCharType.Instance;
  63.         }
  64.        
  65.         //
  66.         // Internal methods and properties
  67.         //
  68.         internal char QuoteChar {
  69.             set { this.quoteChar = value; }
  70.         }
  71.        
  72.         internal void StartAttribute(bool cacheAttrValue)
  73.         {
  74.             this.inAttribute = true;
  75.             this.cacheAttrValue = cacheAttrValue;
  76.             if (cacheAttrValue) {
  77.                 if (attrValue == null) {
  78.                     attrValue = new BufferBuilder();
  79.                 }
  80.                 else {
  81.                     attrValue.Clear();
  82.                 }
  83.             }
  84.         }
  85.        
  86.         internal void EndAttribute()
  87.         {
  88.             if (cacheAttrValue) {
  89.                 attrValue.Clear();
  90.             }
  91.             this.inAttribute = false;
  92.             this.cacheAttrValue = false;
  93.         }
  94.        
  95.         internal string AttributeValue {
  96.             get {
  97.                 if (cacheAttrValue) {
  98.                     return attrValue.ToString();
  99.                 }
  100.                 else {
  101.                     return String.Empty;
  102.                 }
  103.             }
  104.         }
  105.        
  106.         internal void WriteSurrogateChar(char lowChar, char highChar)
  107.         {
  108.             if (((int)lowChar < SurLowStart || (int)lowChar > SurLowEnd) || ((int)highChar < SurHighStart || (int)highChar > SurHighEnd)) {
  109.                 throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
  110.             }
  111.            
  112.             textWriter.Write(highChar);
  113.             textWriter.Write(lowChar);
  114.         }
  115.        
  116.         internal void Write(char[] array, int offset, int count)
  117.         {
  118.             if (null == array) {
  119.                 throw new ArgumentNullException("array");
  120.             }
  121.            
  122.             if (0 > offset) {
  123.                 throw new ArgumentOutOfRangeException("offset");
  124.             }
  125.            
  126.             if (0 > count) {
  127.                 throw new ArgumentOutOfRangeException("count");
  128.             }
  129.            
  130.             if (count > array.Length - offset) {
  131.                 throw new ArgumentOutOfRangeException("count");
  132.             }
  133.            
  134.             if (cacheAttrValue) {
  135.                 attrValue.Append(array, offset, count);
  136.             }
  137.            
  138.             int endPos = offset + count;
  139.             int i = offset;
  140.             char ch = (char)0;
  141.             for (;;) {
  142.                 int startPos = i;
  143.                 unsafe {
  144.                     while (i < endPos && (xmlCharType.charProperties[ch = array[i]] & XmlCharType.fAttrValue) != 0) {
  145.                         // ( xmlCharType.IsAttributeValueChar( ( ch = array[i] ) ) ) ) {
  146.                         i++;
  147.                     }
  148.                 }
  149.                
  150.                 if (startPos < i) {
  151.                     textWriter.Write(array, startPos, i - startPos);
  152.                 }
  153.                 if (i == endPos) {
  154.                     break;
  155.                 }
  156.                
  157.                 switch (ch) {
  158.                     case (char)9:
  159.                         textWriter.Write(ch);
  160.                         break;
  161.                     case (char)10:
  162.                     case (char)13:
  163.                         if (inAttribute) {
  164.                             WriteCharEntityImpl(ch);
  165.                         }
  166.                         else {
  167.                             textWriter.Write(ch);
  168.                         }
  169.                         break;
  170.                     case '<':
  171.                        
  172.                         WriteEntityRefImpl("lt");
  173.                         break;
  174.                     case '>':
  175.                         WriteEntityRefImpl("gt");
  176.                         break;
  177.                     case '&':
  178.                         WriteEntityRefImpl("amp");
  179.                         break;
  180.                     case '\'':
  181.                         if (inAttribute && quoteChar == ch) {
  182.                             WriteEntityRefImpl("apos");
  183.                         }
  184.                         else {
  185.                             textWriter.Write('\'');
  186.                         }
  187.                         break;
  188.                     case '"':
  189.                         if (inAttribute && quoteChar == ch) {
  190.                             WriteEntityRefImpl("quot");
  191.                         }
  192.                         else {
  193.                             textWriter.Write('"');
  194.                         }
  195.                         break;
  196.                     default:
  197.                         if ((int)ch >= SurHighStart && (int)ch <= SurHighEnd) {
  198.                             if (i + 1 < endPos) {
  199.                                 WriteSurrogateChar(array[++i], ch);
  200.                             }
  201.                             else {
  202.                                 throw new ArgumentException(Res.GetString(Res.Xml_SurrogatePairSplit));
  203.                             }
  204.                         }
  205.                         else if ((int)ch >= SurLowStart && (int)ch <= SurLowEnd) {
  206.                             throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
  207.                         }
  208.                         else {
  209.                             Debug.Assert((ch < 32 && !xmlCharType.IsWhiteSpace(ch)) || (ch > 65533));
  210.                             WriteCharEntityImpl(ch);
  211.                         }
  212.                         break;
  213.                 }
  214.                 i++;
  215.             }
  216.         }
  217.        
  218.         internal void Write(char ch)
  219.         {
  220.             if (cacheAttrValue) {
  221.                 attrValue.Append(ch);
  222.             }
  223.            
  224.             bool isAttrValue;
  225.             unsafe {
  226.                 isAttrValue = (xmlCharType.charProperties[ch] & XmlCharType.fAttrValue) != 0;
  227.                 // xmlCharType.IsAttributeValueChar( ch )
  228.             }
  229.             if (isAttrValue) {
  230.                 textWriter.Write(ch);
  231.             }
  232.             else {
  233.                 switch (ch) {
  234.                     case (char)9:
  235.                         textWriter.Write(ch);
  236.                         break;
  237.                     case (char)10:
  238.                     case (char)13:
  239.                         if (inAttribute) {
  240.                             WriteCharEntityImpl(ch);
  241.                         }
  242.                         else {
  243.                             textWriter.Write(ch);
  244.                         }
  245.                         break;
  246.                     case '<':
  247.                         WriteEntityRefImpl("lt");
  248.                         break;
  249.                     case '>':
  250.                         WriteEntityRefImpl("gt");
  251.                         break;
  252.                     case '&':
  253.                         WriteEntityRefImpl("amp");
  254.                         break;
  255.                     case '\'':
  256.                         if (inAttribute && quoteChar == ch) {
  257.                             WriteEntityRefImpl("apos");
  258.                         }
  259.                         else {
  260.                             textWriter.Write('\'');
  261.                         }
  262.                         break;
  263.                     case '"':
  264.                         if (inAttribute && quoteChar == ch) {
  265.                             WriteEntityRefImpl("quot");
  266.                         }
  267.                         else {
  268.                             textWriter.Write('"');
  269.                         }
  270.                         break;
  271.                     default:
  272.                         if ((int)ch >= SurHighStart && (int)ch <= SurHighEnd) {
  273.                             throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
  274.                         }
  275.                         else if ((int)ch >= SurLowStart && (int)ch <= SurLowEnd) {
  276.                             throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
  277.                         }
  278.                         else {
  279.                             Debug.Assert((ch < 32 && !xmlCharType.IsWhiteSpace(ch)) || (ch > 65533));
  280.                             WriteCharEntityImpl(ch);
  281.                         }
  282.                         break;
  283.                 }
  284.             }
  285.         }
  286.        
  287.         internal void WriteSurrogateCharEntity(char lowChar, char highChar)
  288.         {
  289.             if (((int)lowChar < SurLowStart || (int)lowChar > SurLowEnd) || ((int)highChar < SurHighStart || (int)highChar > SurHighEnd)) {
  290.                 throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
  291.                
  292.             }
  293.             int surrogateChar = ((int)lowChar - SurLowStart) | (((int)highChar - SurHighStart) << 10) + 65536;
  294.            
  295.             if (cacheAttrValue) {
  296.                 attrValue.Append(highChar);
  297.                 attrValue.Append(lowChar);
  298.             }
  299.            
  300.             textWriter.Write("&#x");
  301.             textWriter.Write(surrogateChar.ToString("X", NumberFormatInfo.InvariantInfo));
  302.             textWriter.Write(';');
  303.         }
  304.        
  305.         internal void Write(string text)
  306.         {
  307.             if (text == null) {
  308.                 return;
  309.             }
  310.            
  311.             if (cacheAttrValue) {
  312.                 attrValue.Append(text);
  313.             }
  314.            
  315.             // scan through the string to see if there are any characters to be escaped
  316.             int len = text.Length;
  317.             int i = 0;
  318.             int startPos = 0;
  319.             char ch = (char)0;
  320.             for (;;) {
  321.                 unsafe {
  322.                     while (i < len && (xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue) != 0) {
  323.                         // ( xmlCharType.IsAttributeValueChar( ( ch = text[i] ) ) ) ) {
  324.                         i++;
  325.                     }
  326.                 }
  327.                 if (i == len) {
  328.                     // reached the end of the string -> write it whole out
  329.                     textWriter.Write(text);
  330.                     return;
  331.                 }
  332.                 if (inAttribute) {
  333.                     if (ch == 9) {
  334.                         i++;
  335.                         continue;
  336.                     }
  337.                 }
  338.                 else {
  339.                     if (ch == 9 || ch == 10 || ch == 13 || ch == '"' || ch == '\'') {
  340.                         i++;
  341.                         continue;
  342.                     }
  343.                 }
  344.                 // some character that needs to be escaped is found:
  345.                 break;
  346.             }
  347.            
  348.             char[] helperBuffer = new char[256];
  349.             for (;;) {
  350.                 if (startPos < i) {
  351.                     WriteStringFragment(text, startPos, i - startPos, helperBuffer);
  352.                 }
  353.                 if (i == len) {
  354.                     break;
  355.                 }
  356.                
  357.                 switch (ch) {
  358.                     case (char)9:
  359.                         textWriter.Write(ch);
  360.                         break;
  361.                     case (char)10:
  362.                     case (char)13:
  363.                         if (inAttribute) {
  364.                             WriteCharEntityImpl(ch);
  365.                         }
  366.                         else {
  367.                             textWriter.Write(ch);
  368.                         }
  369.                         break;
  370.                     case '<':
  371.                         WriteEntityRefImpl("lt");
  372.                         break;
  373.                     case '>':
  374.                         WriteEntityRefImpl("gt");
  375.                         break;
  376.                     case '&':
  377.                         WriteEntityRefImpl("amp");
  378.                         break;
  379.                     case '\'':
  380.                         if (inAttribute && quoteChar == ch) {
  381.                             WriteEntityRefImpl("apos");
  382.                         }
  383.                         else {
  384.                             textWriter.Write('\'');
  385.                         }
  386.                         break;
  387.                     case '"':
  388.                         if (inAttribute && quoteChar == ch) {
  389.                             WriteEntityRefImpl("quot");
  390.                         }
  391.                         else {
  392.                             textWriter.Write('"');
  393.                         }
  394.                         break;
  395.                     default:
  396.                         if ((int)ch >= SurHighStart && (int)ch <= SurHighEnd) {
  397.                             if (i + 1 < len) {
  398.                                 WriteSurrogateChar(text[++i], ch);
  399.                             }
  400.                             else {
  401.                                 throw XmlConvert.CreateInvalidSurrogatePairException(text[i], ch);
  402.                             }
  403.                         }
  404.                         else if ((int)ch >= SurLowStart && (int)ch <= SurLowEnd) {
  405.                             throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
  406.                         }
  407.                         else {
  408.                             Debug.Assert((ch < 32 && !xmlCharType.IsWhiteSpace(ch)) || (ch > 65533));
  409.                             WriteCharEntityImpl(ch);
  410.                         }
  411.                         break;
  412.                 }
  413.                 i++;
  414.                 startPos = i;
  415.                 unsafe {
  416.                     while (i < len && (xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue) != 0) {
  417.                         // ( xmlCharType.IsAttributeValueChar( ( text[i] ) ) ) ) {
  418.                         i++;
  419.                     }
  420.                 }
  421.             }
  422.         }
  423.        
  424.         internal void WriteRawWithSurrogateChecking(string text)
  425.         {
  426.             if (text == null) {
  427.                 return;
  428.             }
  429.             if (cacheAttrValue) {
  430.                 attrValue.Append(text);
  431.             }
  432.            
  433.             int len = text.Length;
  434.             int i = 0;
  435.             char ch = (char)0;
  436.            
  437.             for (;;) {
  438.                 unsafe {
  439.                     // ( xmlCharType.IsCharData( ( ch = text[i] ) )
  440.                     while (i < len && ((xmlCharType.charProperties[ch = text[i]] & XmlCharType.fCharData) != 0 || ch < 32)) {
  441.                         i++;
  442.                     }
  443.                 }
  444.                 if (i == len) {
  445.                     break;
  446.                 }
  447.                 if ((int)ch >= SurHighStart && (int)ch <= SurHighEnd) {
  448.                     if (i + 1 < len) {
  449.                         char lowChar = text[i + 1];
  450.                         if (lowChar >= SurLowStart && lowChar <= SurLowEnd) {
  451.                             i += 2;
  452.                             continue;
  453.                         }
  454.                         else {
  455.                             throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, ch);
  456.                         }
  457.                     }
  458.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
  459.                 }
  460.                 else if ((int)ch >= SurLowStart && (int)ch <= SurLowEnd) {
  461.                     throw XmlConvert.CreateInvalidHighSurrogateCharException(ch);
  462.                 }
  463.                 else {
  464.                     i++;
  465.                 }
  466.             }
  467.            
  468.             textWriter.Write(text);
  469.             return;
  470.         }
  471.        
  472.         internal void WriteRaw(string value)
  473.         {
  474.             if (cacheAttrValue) {
  475.                 attrValue.Append(value);
  476.             }
  477.             textWriter.Write(value);
  478.         }
  479.        
  480.         internal void WriteRaw(char[] array, int offset, int count)
  481.         {
  482.             if (null == array) {
  483.                 throw new ArgumentNullException("array");
  484.             }
  485.            
  486.             if (0 > count) {
  487.                 throw new ArgumentOutOfRangeException("count");
  488.             }
  489.            
  490.             if (0 > offset) {
  491.                 throw new ArgumentOutOfRangeException("offset");
  492.             }
  493.            
  494.             if (count > array.Length - offset) {
  495.                 throw new ArgumentOutOfRangeException("count");
  496.             }
  497.            
  498.             if (cacheAttrValue) {
  499.                 attrValue.Append(array, offset, count);
  500.             }
  501.             textWriter.Write(array, offset, count);
  502.         }
  503.        
  504.        
  505.        
  506.         internal void WriteCharEntity(char ch)
  507.         {
  508.             if ((int)ch >= SurHighStart && (int)ch <= SurLowEnd) {
  509.                 throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
  510.             }
  511.            
  512.             string strVal = ((int)ch).ToString("X", NumberFormatInfo.InvariantInfo);
  513.             if (cacheAttrValue) {
  514.                 attrValue.Append("&#x");
  515.                 attrValue.Append(strVal);
  516.                 attrValue.Append(';');
  517.             }
  518.             WriteCharEntityImpl(strVal);
  519.         }
  520.        
  521.         internal void WriteEntityRef(string name)
  522.         {
  523.             if (cacheAttrValue) {
  524.                 attrValue.Append('&');
  525.                 attrValue.Append(name);
  526.                 attrValue.Append(';');
  527.             }
  528.             WriteEntityRefImpl(name);
  529.         }
  530.        
  531.         internal void Flush()
  532.         {
  533.         }
  534.        
  535.         //
  536.         // Private implementation methods
  537.         //
  538.         // This is a helper method to woraround the fact that TextWriter does not have a Write method
  539.         // for fragment of a string such as Write( string, offset, count).
  540.         // The string fragment will be written out by copying into a small helper buffer and then
  541.         // calling textWriter to write out the buffer.
  542.         private void WriteStringFragment(string str, int offset, int count, char[] helperBuffer)
  543.         {
  544.             int bufferSize = helperBuffer.Length;
  545.             while (count > 0) {
  546.                 int copyCount = count;
  547.                 if (copyCount > bufferSize) {
  548.                     copyCount = bufferSize;
  549.                 }
  550.                
  551.                 str.CopyTo(offset, helperBuffer, 0, copyCount);
  552.                 textWriter.Write(helperBuffer, 0, copyCount);
  553.                 offset += copyCount;
  554.                 count -= copyCount;
  555.             }
  556.         }
  557.        
  558.         private void WriteCharEntityImpl(char ch)
  559.         {
  560.             WriteCharEntityImpl(((int)ch).ToString("X", NumberFormatInfo.InvariantInfo));
  561.         }
  562.        
  563.         private void WriteCharEntityImpl(string strVal)
  564.         {
  565.             textWriter.Write("&#x");
  566.             textWriter.Write(strVal);
  567.             textWriter.Write(';');
  568.         }
  569.        
  570.         private void WriteEntityRefImpl(string name)
  571.         {
  572.             textWriter.Write('&');
  573.             textWriter.Write(name);
  574.             textWriter.Write(';');
  575.         }
  576.     }
  577. }

Developer Fusion