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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="HtmlRawTextWriterGenerator.cxx" 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. // WARNING: This file is generated and should not be modified directly. Instead,
  16. // modify XmlTextWriterGenerator.cxx and run gen.bat in the same directory.
  17. // This batch file will execute the following commands:
  18. //
  19. // cl.exe /C /EP /D _XML_UTF8_TEXT_WRITER HtmlTextWriterGenerator.cxx > HtmlUtf8TextWriter.cs
  20. // cl.exe /C /EP /D _XML_ENCODED_TEXT_WRITER HtmlTextWriterGenerator.cxx > HtmlEncodedTextWriter.cs
  21. //
  22. // Because these two implementations of XmlTextWriter are so similar, the C++ preprocessor
  23. // is used to generate each implementation from one template file, using macros and ifdefs.
  24. using System;
  25. using System.IO;
  26. using System.Text;
  27. using System.Xml;
  28. using System.Xml.Schema;
  29. //using System.Xml.Query;
  30. using System.Diagnostics;
  31. using MS.Internal.Xml;
  32. namespace System.Xml
  33. {
  34.    
  35.     internal class HtmlUtf8RawTextWriter : XmlUtf8RawTextWriter
  36.     {
  37.         //
  38.         // Fields
  39.         //
  40.         protected ByteStack elementScope;
  41.         protected ElementProperties currentElementProperties;
  42.         private AttributeProperties currentAttributeProperties;
  43.        
  44.         private bool endsWithAmpersand;
  45.         private byte[] uriEscapingBuffer;
  46.        
  47.         // settings
  48.         private string mediaType;
  49.        
  50.         //
  51.         // Static fields
  52.         //
  53.         protected static TernaryTreeReadOnly elementPropertySearch;
  54.         protected static TernaryTreeReadOnly attributePropertySearch;
  55.        
  56.         //
  57.         // Constants
  58.         //
  59.         private const int StackIncrement = 10;
  60.        
  61.         //
  62.         // Constructors
  63.         //
  64.        
  65.        
  66.        
  67.        
  68.        
  69.        
  70.        
  71.        
  72.         public HtmlUtf8RawTextWriter(Stream stream, Encoding encoding, XmlWriterSettings settings, bool closeOutput) : base(stream, encoding, settings, closeOutput)
  73.         {
  74.             Init(settings);
  75.         }
  76.        
  77.         //
  78.         // XmlRawWriter implementation
  79.         //
  80.         internal override void WriteXmlDeclaration(XmlStandalone standalone)
  81.         {
  82.             // Ignore xml declaration
  83.         }
  84.        
  85.         internal override void WriteXmlDeclaration(string xmldecl)
  86.         {
  87.             // Ignore xml declaration
  88.         }
  89.        
  90.         /// Html rules allow public ID without system ID and always output "html"
  91.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  92.         {
  93.             Debug.Assert(name != null && name.Length > 0);
  94.            
  95.            
  96.            
  97.             RawText("<!DOCTYPE ");
  98.            
  99.             // Bug 114337: Always output "html" or "HTML" in doc-type, even if "name" is something else
  100.             if (name == "HTML")
  101.                 RawText("HTML");
  102.             else
  103.                 RawText("html");
  104.            
  105.             if (pubid != null) {
  106.                 RawText(" PUBLIC \"");
  107.                 RawText(pubid);
  108.                 if (sysid != null) {
  109.                     RawText("\" \"");
  110.                     RawText(sysid);
  111.                 }
  112.                 bufBytes[bufPos++] = (byte)'"';
  113.             }
  114.             else if (sysid != null) {
  115.                 RawText(" SYSTEM \"");
  116.                 RawText(sysid);
  117.                 bufBytes[bufPos++] = (byte)'"';
  118.             }
  119.             else {
  120.                 bufBytes[bufPos++] = (byte)' ';
  121.             }
  122.            
  123.             if (subset != null) {
  124.                 bufBytes[bufPos++] = (byte)'[';
  125.                 RawText(subset);
  126.                 bufBytes[bufPos++] = (byte)']';
  127.             }
  128.            
  129.             bufBytes[this.bufPos++] = (byte)'>';
  130.         }
  131.        
  132.         // For the HTML element, it should call this method with ns and prefix as String.Empty
  133.         public override void WriteStartElement(string prefix, string localName, string ns)
  134.         {
  135.             Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
  136.            
  137.             elementScope.Push((byte)currentElementProperties);
  138.            
  139.             if (ns.Length == 0) {
  140.                 Debug.Assert(prefix.Length == 0);
  141.                
  142.                
  143.                 currentElementProperties = (ElementProperties)elementPropertySearch.FindCaseInsensitiveString(localName);
  144.                 base.bufBytes[bufPos++] = (byte)'<';
  145.                 base.RawText(localName);
  146.                 base.attrEndPos = bufPos;
  147.             }
  148.             else {
  149.                 // Since the HAS_NS has no impact to the ElementTextBlock behavior,
  150.                 // we don't need to push it into the stack.
  151.                 currentElementProperties = ElementProperties.HAS_NS;
  152.                 base.WriteStartElement(prefix, localName, ns);
  153.             }
  154.         }
  155.        
  156.         // Output >. For HTML needs to output META info
  157.         internal override void StartElementContent()
  158.         {
  159.             base.bufBytes[base.bufPos++] = (byte)'>';
  160.            
  161.             // Detect whether content is output
  162.             this.contentPos = this.bufPos;
  163.            
  164.             if ((currentElementProperties & ElementProperties.HEAD) != 0) {
  165.                 WriteMetaElement();
  166.             }
  167.         }
  168.        
  169.         // end element with />
  170.         // for HTML(ns.Length == 0)
  171.         // not an empty tag <h1></h1>
  172.         // empty tag <basefont>
  173.         internal override void WriteEndElement(string prefix, string localName, string ns)
  174.         {
  175.             if (ns.Length == 0) {
  176.                 Debug.Assert(prefix.Length == 0);
  177.                
  178.                
  179.                
  180.                 if ((currentElementProperties & ElementProperties.EMPTY) == 0) {
  181.                     bufBytes[base.bufPos++] = (byte)'<';
  182.                     bufBytes[base.bufPos++] = (byte)'/';
  183.                     base.RawText(localName);
  184.                     bufBytes[base.bufPos++] = (byte)'>';
  185.                 }
  186.             }
  187.             else {
  188.                 //xml content
  189.                 base.WriteEndElement(prefix, localName, ns);
  190.             }
  191.            
  192.             currentElementProperties = (ElementProperties)elementScope.Pop();
  193.         }
  194.        
  195.         internal override void WriteFullEndElement(string prefix, string localName, string ns)
  196.         {
  197.             if (ns.Length == 0) {
  198.                 Debug.Assert(prefix.Length == 0);
  199.                
  200.                
  201.                
  202.                 if ((currentElementProperties & ElementProperties.EMPTY) == 0) {
  203.                     bufBytes[base.bufPos++] = (byte)'<';
  204.                     bufBytes[base.bufPos++] = (byte)'/';
  205.                     base.RawText(localName);
  206.                     bufBytes[base.bufPos++] = (byte)'>';
  207.                 }
  208.             }
  209.             else {
  210.                 //xml content
  211.                 base.WriteFullEndElement(prefix, localName, ns);
  212.             }
  213.            
  214.             currentElementProperties = (ElementProperties)elementScope.Pop();
  215.         }
  216.        
  217.         // 1. How the outputBooleanAttribute(fBOOL) and outputHtmlUriText(fURI) being set?
  218.         // When SA is called.
  219.         //
  220.         // BOOL_PARENT URI_PARENT Others
  221.         // fURI
  222.         // URI att false true false
  223.         //
  224.         // fBOOL
  225.         // BOOL att true false false
  226.         //
  227.         // How they change the attribute output behaviors?
  228.         //
  229.         // 1) fURI=true fURI=false
  230.         // SA a=" a="
  231.         // AT HtmlURIText HtmlText
  232.         // EA " "
  233.         //
  234.         // 2) fBOOL=true fBOOL=false
  235.         // SA a a="
  236.         // AT HtmlText output nothing
  237.         // EA output nothing "
  238.         //
  239.         // When they get reset?
  240.         // At the end of attribute.
  241.        
  242.         // 2. How the outputXmlTextElementScoped(fENs) and outputXmlTextattributeScoped(fANs) are set?
  243.         // fANs is in the scope of the fENs.
  244.         //
  245.         // SE(localName) SE(ns, pre, localName) SA(localName) SA(ns, pre, localName)
  246.         // fENs false(default) true(action)
  247.         // fANs false(default) false(default) false(default) true(action)
  248.        
  249.         // how they get reset?
  250.         //
  251.         // EE(localName) EE(ns, pre, localName) EENC(ns, pre, localName) EA(localName) EA(ns, pre, localName)
  252.         // fENs false(action)
  253.         // fANs false(action)
  254.        
  255.         // How they change the TextOutput?
  256.         //
  257.         // fENs | fANs Else
  258.         // AT XmlText HtmlText
  259.         //
  260.         //
  261.         // 3. Flags for processing &{ split situations
  262.         //
  263.         // When the flag is set?
  264.         //
  265.         // AT src[lastchar]='&' flag&{ = true;
  266.         //
  267.         // when it get result?
  268.         //
  269.         // AT method.
  270.         //
  271.         // How it changes the behaviors?
  272.         //
  273.         // flag&{=true
  274.         //
  275.         // AT if (src[0] == '{') {
  276.         // output "&{"
  277.         // }
  278.         // else {
  279.         // output &amp;
  280.         // }
  281.         //
  282.         // EA output amp;
  283.         //
  284.        
  285.         // SA if (flagBOOL == false) { output =";}
  286.         //
  287.         // AT if (flagBOOL) { return};
  288.         // if (flagNS) {XmlText;} {
  289.         // }
  290.         // else if (flagURI) {
  291.         // HtmlURIText;
  292.         // }
  293.         // else {
  294.         // HtmlText;
  295.         // }
  296.         //
  297.        
  298.         // AT if (flagNS) {XmlText;} {
  299.         // }
  300.         // else if (flagURI) {
  301.         // HtmlURIText;
  302.         // }
  303.         // else if (!flagBOOL) {
  304.         // HtmlText; //flag&{ handling
  305.         // }
  306.         //
  307.         //
  308.         // EA if (flagBOOL == false) { output "
  309.         // }
  310.         // else if (flag&{) {
  311.         // output amp;
  312.         // }
  313.         //
  314.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  315.         {
  316.             Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
  317.            
  318.             if (ns.Length == 0) {
  319.                 Debug.Assert(prefix.Length == 0);
  320.                
  321.                
  322.                 if (base.attrEndPos == bufPos) {
  323.                     base.bufBytes[bufPos++] = (byte)' ';
  324.                 }
  325.                 base.RawText(localName);
  326.                
  327.                 if ((currentElementProperties & (ElementProperties.BOOL_PARENT | ElementProperties.URI_PARENT | ElementProperties.NAME_PARENT)) != 0) {
  328.                     currentAttributeProperties = (AttributeProperties)attributePropertySearch.FindCaseInsensitiveString(localName) & (AttributeProperties)currentElementProperties;
  329.                    
  330.                     if ((currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) {
  331.                         base.inAttributeValue = true;
  332.                         return;
  333.                     }
  334.                 }
  335.                 else {
  336.                     currentAttributeProperties = AttributeProperties.DEFAULT;
  337.                 }
  338.                
  339.                 base.bufBytes[bufPos++] = (byte)'=';
  340.                 base.bufBytes[bufPos++] = (byte)'"';
  341.             }
  342.             else {
  343.                 base.WriteStartAttribute(prefix, localName, ns);
  344.                 currentAttributeProperties = AttributeProperties.DEFAULT;
  345.             }
  346.            
  347.             base.inAttributeValue = true;
  348.         }
  349.        
  350.         // Output the amp; at end of EndAttribute
  351.         public override void WriteEndAttribute()
  352.         {
  353.            
  354.             if ((currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) {
  355.                 base.attrEndPos = bufPos;
  356.             }
  357.             else {
  358.                 if (endsWithAmpersand) {
  359.                     OutputRestAmps();
  360.                     endsWithAmpersand = false;
  361.                 }
  362.                
  363.                
  364.                
  365.                 base.bufBytes[bufPos++] = (byte)'"';
  366.             }
  367.             base.inAttributeValue = false;
  368.             base.attrEndPos = bufPos;
  369.         }
  370.        
  371.         // HTML PI's use ">" to terminate rather than "?>".
  372.         public override void WriteProcessingInstruction(string target, string text)
  373.         {
  374.             Debug.Assert(target != null && target.Length != 0 && text != null);
  375.            
  376.            
  377.            
  378.             bufBytes[base.bufPos++] = (byte)'<';
  379.             bufBytes[base.bufPos++] = (byte)'?';
  380.             base.RawText(target);
  381.             bufBytes[base.bufPos++] = (byte)' ';
  382.            
  383.             base.WriteCommentOrPi(text, '?');
  384.            
  385.             base.bufBytes[base.bufPos++] = (byte)'>';
  386.            
  387.             if (base.bufPos > base.bufLen) {
  388.                 FlushBuffer();
  389.             }
  390.         }
  391.        
  392.         // Serialize either attribute or element text using HTML rules.
  393.         unsafe public override void WriteString(string text)
  394.         {
  395.             Debug.Assert(text != null);
  396.            
  397.            
  398.            
  399.             fixed (char* pSrc = text) {
  400.                 char* pSrcEnd = pSrc + text.Length;
  401.                 if (base.inAttributeValue) {
  402.                     WriteHtmlAttributeTextBlock(pSrc, pSrcEnd);
  403.                 }
  404.                 else {
  405.                     WriteHtmlElementTextBlock(pSrc, pSrcEnd);
  406.                 }
  407.             }
  408.         }
  409.        
  410.         public override void WriteEntityRef(string name)
  411.         {
  412.             throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
  413.         }
  414.        
  415.         public override void WriteCharEntity(char ch)
  416.         {
  417.             throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
  418.         }
  419.        
  420.         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  421.         {
  422.             throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
  423.         }
  424.        
  425.         unsafe public override void WriteChars(char[] buffer, int index, int count)
  426.         {
  427.             Debug.Assert(buffer != null);
  428.             Debug.Assert(index >= 0);
  429.             Debug.Assert(count >= 0 && index + count <= buffer.Length);
  430.            
  431.            
  432.            
  433.             fixed (char* pSrcBegin = &buffer[index]) {
  434.                 if (inAttributeValue) {
  435.                     WriteAttributeTextBlock(pSrcBegin, pSrcBegin + count);
  436.                 }
  437.                 else {
  438.                     WriteElementTextBlock(pSrcBegin, pSrcBegin + count);
  439.                 }
  440.             }
  441.         }
  442.        
  443.         //
  444.         // Private methods
  445.         //
  446.        
  447.         private void Init(XmlWriterSettings settings)
  448.         {
  449.             Debug.Assert((int)ElementProperties.URI_PARENT == (int)AttributeProperties.URI);
  450.             Debug.Assert((int)ElementProperties.BOOL_PARENT == (int)AttributeProperties.BOOLEAN);
  451.             Debug.Assert((int)ElementProperties.NAME_PARENT == (int)AttributeProperties.NAME);
  452.            
  453.             if (elementPropertySearch == null) {
  454.                 //elementPropertySearch should be init last for the mutli thread safe situation.
  455.                 attributePropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlAttributes);
  456.                 elementPropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlElements);
  457.             }
  458.            
  459.             elementScope = new ByteStack(StackIncrement);
  460.             uriEscapingBuffer = new byte[5];
  461.             currentElementProperties = ElementProperties.DEFAULT;
  462.            
  463.             mediaType = settings.MediaType;
  464.         }
  465.        
  466.         protected void WriteMetaElement()
  467.         {
  468.             base.RawText("<META http-equiv=\"Content-Type\"");
  469.            
  470.             if (mediaType == null) {
  471.                 mediaType = "text/html";
  472.             }
  473.            
  474.             base.RawText(" content=\"");
  475.             base.RawText(mediaType);
  476.             base.RawText("; charset=");
  477.             base.RawText(base.encoding.WebName);
  478.             base.RawText("\">");
  479.         }
  480.        
  481.         // Justify the stack usage:
  482.         //
  483.         // Nested elements has following possible position combinations
  484.         // 1. <E1>Content1<E2>Content2</E2></E1>
  485.         // 2. <E1><E2>Content2</E2>Content1</E1>
  486.         // 3. <E1>Content<E2>Cotent2</E2>Content1</E1>
  487.         //
  488.         // In the situation 2 and 3, the stored currentElementProrperties will be E2's,
  489.         // only the top of the stack is the real E1 element properties.
  490.         unsafe protected void WriteHtmlElementTextBlock(char* pSrc, char* pSrcEnd)
  491.         {
  492.             if ((currentElementProperties & ElementProperties.NO_ENTITIES) != 0) {
  493.                 base.RawText(pSrc, pSrcEnd);
  494.             }
  495.             else {
  496.                 base.WriteElementTextBlock(pSrc, pSrcEnd);
  497.             }
  498.            
  499.         }
  500.        
  501.         unsafe protected void WriteHtmlAttributeTextBlock(char* pSrc, char* pSrcEnd)
  502.         {
  503.             if ((currentAttributeProperties & (AttributeProperties.BOOLEAN | AttributeProperties.URI | AttributeProperties.NAME)) != 0) {
  504.                 if ((currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) {
  505.                     //if output boolean attribute, ignore this call.
  506.                     return;
  507.                 }
  508.                
  509.                 if ((currentAttributeProperties & (AttributeProperties.URI | AttributeProperties.NAME)) != 0) {
  510.                     WriteUriAttributeText(pSrc, pSrcEnd);
  511.                 }
  512.                 else {
  513.                     WriteHtmlAttributeText(pSrc, pSrcEnd);
  514.                 }
  515.             }
  516.             else if ((currentElementProperties & ElementProperties.HAS_NS) != 0) {
  517.                 base.WriteAttributeTextBlock(pSrc, pSrcEnd);
  518.             }
  519.             else {
  520.                 WriteHtmlAttributeText(pSrc, pSrcEnd);
  521.             }
  522.         }
  523.        
  524.         //
  525.         // &{ split cases
  526.         // 1). HtmlAttributeText("a&");
  527.         // HtmlAttributeText("{b}");
  528.         //
  529.         // 2). HtmlAttributeText("a&");
  530.         // EndAttribute();
  531.        
  532.         // 3).split with Flush by the user
  533.         // HtmlAttributeText("a&");
  534.         // FlushBuffer();
  535.         // HtmlAttributeText("{b}");
  536.        
  537.         //
  538.         // Solutions:
  539.         // case 1)hold the &amp; output as &
  540.         // if the next income character is {, output {
  541.         // else output amp;
  542.         //
  543.        
  544.         unsafe private void WriteHtmlAttributeText(char* pSrc, char* pSrcEnd)
  545.         {
  546.             if (endsWithAmpersand) {
  547.                 if (pSrcEnd - pSrc > 0 && pSrc[0] != '{') {
  548.                     OutputRestAmps();
  549.                 }
  550.                 endsWithAmpersand = false;
  551.             }
  552.            
  553.             fixed (byte* pDstBegin = bufBytes) {
  554.                 byte* pDst = pDstBegin + this.bufPos;
  555.                
  556.                 char ch = (char)0;
  557.                 for (;;) {
  558.                     byte* pDstEnd = pDst + (pSrcEnd - pSrc);
  559.                     if (pDstEnd > pDstBegin + bufLen) {
  560.                         pDstEnd = pDstBegin + bufLen;
  561.                     }
  562.                    
  563.                    
  564.                     while (pDst < pDstEnd && (((xmlCharType.charProperties[(ch = *pSrc)] & XmlCharType.fAttrValue) != 0) && ch <= 127)) {
  565.                        
  566.                        
  567.                        
  568.                         *pDst++ = (byte)ch;
  569.                         pSrc++;
  570.                     }
  571.                     Debug.Assert(pSrc <= pSrcEnd);
  572.                    
  573.                     // end of value
  574.                     if (pSrc >= pSrcEnd) {
  575.                         break;
  576.                     }
  577.                    
  578.                     // end of buffer
  579.                     if (pDst >= pDstEnd) {
  580.                         bufPos = (int)(pDst - pDstBegin);
  581.                         FlushBuffer();
  582.                         pDst = pDstBegin + 1;
  583.                         continue;
  584.                     }
  585.                    
  586.                     // some character needs to be escaped
  587.                     switch (ch) {
  588.                         case '&':
  589.                             if (pSrc + 1 == pSrcEnd) {
  590.                                 endsWithAmpersand = true;
  591.                             }
  592.                             else if (pSrc[1] != '{') {
  593.                                 pDst = XmlUtf8RawTextWriter.AmpEntity(pDst);
  594.                                 break;
  595.                             }
  596.                             *pDst++ = (byte)ch;
  597.                             break;
  598.                         case '"':
  599.                             pDst = QuoteEntity(pDst);
  600.                             break;
  601.                         case '<':
  602.                         case '>':
  603.                         case '\'':
  604.                         case (char)9:
  605.                             *pDst++ = (byte)ch;
  606.                             break;
  607.                         case (char)13:
  608.                             // do not normalize new lines in attributes - just escape them
  609.                             pDst = CarriageReturnEntity(pDst);
  610.                             break;
  611.                         case (char)10:
  612.                             // do not normalize new lines in attributes - just escape them
  613.                             pDst = LineFeedEntity(pDst);
  614.                             break;
  615.                         default:
  616.                             EncodeChar(ref pSrc, pSrcEnd, ref pDst);
  617.                             continue;
  618.                     }
  619.                     pSrc++;
  620.                 }
  621.                 bufPos = (int)(pDst - pDstBegin);
  622.             }
  623.         }
  624.        
  625.         unsafe private void WriteUriAttributeText(char* pSrc, char* pSrcEnd)
  626.         {
  627.             if (endsWithAmpersand) {
  628.                 if (pSrcEnd - pSrc > 0 && pSrc[0] != '{') {
  629.                     OutputRestAmps();
  630.                 }
  631.                 this.endsWithAmpersand = false;
  632.             }
  633.            
  634.             fixed (byte* pDstBegin = bufBytes) {
  635.                 byte* pDst = pDstBegin + this.bufPos;
  636.                
  637.                 char ch = (char)0;
  638.                 for (;;) {
  639.                     byte* pDstEnd = pDst + (pSrcEnd - pSrc);
  640.                     if (pDstEnd > pDstBegin + bufLen) {
  641.                         pDstEnd = pDstBegin + bufLen;
  642.                     }
  643.                    
  644.                     while (pDst < pDstEnd && (((xmlCharType.charProperties[(ch = *pSrc)] & XmlCharType.fAttrValue) != 0) && ch < 128)) {
  645.                         *pDst++ = (byte)ch;
  646.                         pSrc++;
  647.                     }
  648.                     Debug.Assert(pSrc <= pSrcEnd);
  649.                    
  650.                     // end of value
  651.                     if (pSrc >= pSrcEnd) {
  652.                         break;
  653.                     }
  654.                    
  655.                     // end of buffer
  656.                     if (pDst >= pDstEnd) {
  657.                         bufPos = (int)(pDst - pDstBegin);
  658.                         FlushBuffer();
  659.                         pDst = pDstBegin + 1;
  660.                         continue;
  661.                     }
  662.                    
  663.                     // some character needs to be escaped
  664.                     switch (ch) {
  665.                         case '&':
  666.                             if (pSrc + 1 == pSrcEnd) {
  667.                                 endsWithAmpersand = true;
  668.                             }
  669.                             else if (pSrc[1] != '{') {
  670.                                 pDst = XmlUtf8RawTextWriter.AmpEntity(pDst);
  671.                                 break;
  672.                             }
  673.                             *pDst++ = (byte)ch;
  674.                             break;
  675.                         case '"':
  676.                             pDst = QuoteEntity(pDst);
  677.                             break;
  678.                         case '<':
  679.                         case '>':
  680.                         case '\'':
  681.                         case (char)9:
  682.                             *pDst++ = (byte)ch;
  683.                             break;
  684.                         case (char)13:
  685.                             // do not normalize new lines in attributes - just escape them
  686.                             pDst = CarriageReturnEntity(pDst);
  687.                             break;
  688.                         case (char)10:
  689.                             // do not normalize new lines in attributes - just escape them
  690.                             pDst = LineFeedEntity(pDst);
  691.                             break;
  692.                         default:
  693.                             const string hexDigits = "0123456789ABCDEF";
  694.                             fixed (byte* pUriEscapingBuffer = uriEscapingBuffer) {
  695.                                 byte* pByte = pUriEscapingBuffer;
  696.                                 byte* pEnd = pByte;
  697.                                
  698.                                 XmlUtf8RawTextWriter.CharToUTF8(ref pSrc, pSrcEnd, ref pEnd);
  699.                                
  700.                                 while (pByte < pEnd) {
  701.                                     *pDst++ = (byte)'%';
  702.                                     *pDst++ = (byte)hexDigits[*pByte >> 4];
  703.                                     *pDst++ = (byte)hexDigits[*pByte & 15];
  704.                                     pByte++;
  705.                                 }
  706.                             }
  707.                             continue;
  708.                     }
  709.                     pSrc++;
  710.                 }
  711.                 bufPos = (int)(pDst - pDstBegin);
  712.             }
  713.         }
  714.        
  715.         // For handling &{ in Html text field. If & is not followed by {, it still needs to be escaped.
  716.         private void OutputRestAmps()
  717.         {
  718.             base.bufBytes[bufPos++] = (byte)'a';
  719.             base.bufBytes[bufPos++] = (byte)'m';
  720.             base.bufBytes[bufPos++] = (byte)'p';
  721.             base.bufBytes[bufPos++] = (byte)';';
  722.         }
  723.     }
  724.    
  725.    
  726.     //
  727.     // Indentation HtmlWriter only indent <BLOCK><BLOCK> situations
  728.     //
  729.     // Here are all the cases:
  730.     // ELEMENT1 actions ELEMENT2 actions SC EE
  731.     // 1). SE SC store SE blockPro SE a). check ELEMENT1 blockPro <A> </A>
  732.     // EE if SE, EE are blocks b). true: check ELEMENT2 blockPro <B> <B>
  733.     // c). detect ELEMENT is SE, SC
  734.     // d). increase the indexlevel
  735.     //
  736.     // 2). SE SC, Store EE blockPro EE a). check stored blockPro <A></A> </A>
  737.     // EE if SE, EE are blocks b). true: indexLevel same </B>
  738.     //
  739.    
  740.    
  741.     //
  742.     // This is an alternative way to make the output looks better
  743.     //
  744.     // Indentation HtmlWriter only indent <BLOCK><BLOCK> situations
  745.     //
  746.     // Here are all the cases:
  747.     // ELEMENT1 actions ELEMENT2 actions Samples
  748.     // 1). SE SC store SE blockPro SE a). check ELEMENT1 blockPro <A>(blockPos)
  749.     // b). true: check ELEMENT2 blockPro <B>
  750.     // c). detect ELEMENT is SE, SC
  751.     // d). increase the indentLevel
  752.     //
  753.     // 2). EE Store EE blockPro SE a). check stored blockPro </A>
  754.     // b). true: indentLevel same <B>
  755.     // c). output block2
  756.     //
  757.     // 3). EE same as above EE a). check stored blockPro </A>
  758.     // b). true: --indentLevel </B>
  759.     // c). output block2
  760.     //
  761.     // 4). SE SC same as above EE a). check stored blockPro <A></A>
  762.     // b). true: indentLevel no change
  763.     internal class HtmlUtf8RawTextWriterIndent : HtmlUtf8RawTextWriter
  764.     {
  765.         //
  766.         // Fields
  767.         //
  768.         int indentLevel;
  769.        
  770.         // for detecting SE SC sitution
  771.         int endBlockPos;
  772.        
  773.         // settings
  774.         string indentChars;
  775.         bool newLineOnAttributes;
  776.        
  777.         //
  778.         // Constructors
  779.         //
  780.        
  781.        
  782.        
  783.        
  784.        
  785.        
  786.        
  787.         public HtmlUtf8RawTextWriterIndent(Stream stream, Encoding encoding, XmlWriterSettings settings, bool closeOutput) : base(stream, encoding, settings, closeOutput)
  788.         {
  789.             Init(settings);
  790.         }
  791.        
  792.         //
  793.         // XmlRawWriter overrides
  794.         //
  795.         /// <summary>
  796.         /// Serialize the document type declaration.
  797.         /// </summary>
  798.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  799.         {
  800.             base.WriteDocType(name, pubid, sysid, subset);
  801.            
  802.             // Allow indentation after DocTypeDecl
  803.             endBlockPos = base.bufPos;
  804.         }
  805.        
  806.         public override void WriteStartElement(string prefix, string localName, string ns)
  807.         {
  808.             Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
  809.            
  810.            
  811.            
  812.             base.elementScope.Push((byte)base.currentElementProperties);
  813.            
  814.             if (ns.Length == 0) {
  815.                 Debug.Assert(prefix.Length == 0);
  816.                
  817.                 base.currentElementProperties = (ElementProperties)elementPropertySearch.FindCaseInsensitiveString(localName);
  818.                
  819.                 if (endBlockPos == base.bufPos && (base.currentElementProperties & ElementProperties.BLOCK_WS) != 0) {
  820.                     WriteIndent();
  821.                 }
  822.                 indentLevel++;
  823.                
  824.                 base.bufBytes[bufPos++] = (byte)'<';
  825.             }
  826.             else {
  827.                 base.currentElementProperties = ElementProperties.HAS_NS | ElementProperties.BLOCK_WS;
  828.                
  829.                 if (endBlockPos == base.bufPos) {
  830.                     WriteIndent();
  831.                 }
  832.                 indentLevel++;
  833.                
  834.                 base.bufBytes[base.bufPos++] = (byte)'<';
  835.                 if (prefix.Length != 0) {
  836.                     base.RawText(prefix);
  837.                     base.bufBytes[base.bufPos++] = (byte)':';
  838.                 }
  839.             }
  840.             base.RawText(localName);
  841.             base.attrEndPos = bufPos;
  842.         }
  843.        
  844.         internal override void StartElementContent()
  845.         {
  846.             base.bufBytes[base.bufPos++] = (byte)'>';
  847.            
  848.             // Detect whether content is output
  849.             base.contentPos = base.bufPos;
  850.            
  851.             if ((currentElementProperties & ElementProperties.HEAD) != 0) {
  852.                 WriteIndent();
  853.                 WriteMetaElement();
  854.                 endBlockPos = base.bufPos;
  855.             }
  856.             else if ((base.currentElementProperties & ElementProperties.BLOCK_WS) != 0) {
  857.                 // store the element block position
  858.                 endBlockPos = base.bufPos;
  859.             }
  860.         }
  861.        
  862.         internal override void WriteEndElement(string prefix, string localName, string ns)
  863.         {
  864.             bool isBlockWs;
  865.             Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
  866.            
  867.             indentLevel--;
  868.            
  869.             // If this element has block whitespace properties,
  870.             isBlockWs = (base.currentElementProperties & ElementProperties.BLOCK_WS) != 0;
  871.             if (isBlockWs) {
  872.                 // And if the last node to be output had block whitespace properties,
  873.                 // And if content was output within this element,
  874.                 if (endBlockPos == base.bufPos && base.contentPos != base.bufPos) {
  875.                     // Then indent
  876.                     WriteIndent();
  877.                 }
  878.             }
  879.            
  880.             base.WriteEndElement(prefix, localName, ns);
  881.            
  882.             // Reset contentPos in case of empty elements
  883.             base.contentPos = 0;
  884.            
  885.             // Mark end of element in buffer for element's with block whitespace properties
  886.             if (isBlockWs) {
  887.                 endBlockPos = base.bufPos;
  888.             }
  889.         }
  890.        
  891.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  892.         {
  893.             if (newLineOnAttributes) {
  894.                 RawText(base.newLineChars);
  895.                 indentLevel++;
  896.                 WriteIndent();
  897.                 indentLevel--;
  898.             }
  899.             base.WriteStartAttribute(prefix, localName, ns);
  900.         }
  901.        
  902.         protected override void FlushBuffer()
  903.         {
  904.             // Make sure the buffer will reset the block position
  905.             endBlockPos = (endBlockPos == base.bufPos) ? 1 : 0;
  906.             base.FlushBuffer();
  907.         }
  908.        
  909.         //
  910.         // Private methods
  911.         //
  912.         private void Init(XmlWriterSettings settings)
  913.         {
  914.             indentLevel = 0;
  915.             indentChars = settings.IndentChars;
  916.             newLineOnAttributes = settings.NewLineOnAttributes;
  917.         }
  918.        
  919.         private void WriteIndent()
  920.         {
  921.             // <block><inline> -- suppress ws betw <block> and <inline>
  922.             // <block><block> -- don't suppress ws betw <block> and <block>
  923.             // <block>text -- suppress ws betw <block> and text (handled by wcharText method)
  924.             // <block><?PI?> -- suppress ws betw <block> and PI
  925.             // <block><!-- --> -- suppress ws betw <block> and comment
  926.            
  927.             // <inline><block> -- suppress ws betw <inline> and <block>
  928.             // <inline><inline> -- suppress ws betw <inline> and <inline>
  929.             // <inline>text -- suppress ws betw <inline> and text (handled by wcharText method)
  930.             // <inline><?PI?> -- suppress ws betw <inline> and PI
  931.             // <inline><!-- --> -- suppress ws betw <inline> and comment
  932.            
  933.             RawText(base.newLineChars);
  934.             for (int i = indentLevel; i > 0; i--) {
  935.                 RawText(indentChars);
  936.             }
  937.         }
  938.     }
  939. }

Developer Fusion