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

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

Developer Fusion