The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.XsltOld \ SequentialOutput

  1. //------------------------------------------------------------------------------
  2. // <copyright file="SequentialOutput.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. namespace System.Xml.Xsl.XsltOld
  16. {
  17.     using Res = System.Xml.Utils.Res;
  18.     using System;
  19.     using System.Diagnostics;
  20.     using System.Xml;
  21.     using System.Text;
  22.     using System.Collections;
  23.     using System.Globalization;
  24.    
  25.     internal abstract class SequentialOutput : RecordOutput
  26.     {
  27.         private const char s_Colon = ':';
  28.         private const char s_GreaterThan = '>';
  29.         private const char s_LessThan = '<';
  30.         private const char s_Space = ' ';
  31.         private const char s_Quote = '"';
  32.         private const char s_Semicolon = ';';
  33.         private const char s_NewLine = '\n';
  34.         private const char s_Return = '\r';
  35.         private const char s_Ampersand = '&';
  36.         private const string s_LessThanQuestion = "<?";
  37.         private const string s_QuestionGreaterThan = "?>";
  38.         private const string s_LessThanSlash = "</";
  39.         private const string s_SlashGreaterThan = " />";
  40.         private const string s_EqualQuote = "=\"";
  41.         private const string s_DocType = "<!DOCTYPE ";
  42.         private const string s_CommentBegin = "<!--";
  43.         private const string s_CommentEnd = "-->";
  44.         private const string s_CDataBegin = "<![CDATA[";
  45.         private const string s_CDataEnd = "]]>";
  46.         private const string s_VersionAll = " version=\"1.0\"";
  47.         private const string s_Standalone = " standalone=\"";
  48.         private const string s_EncodingStart = " encoding=\"";
  49.         private const string s_Public = "PUBLIC ";
  50.         private const string s_System = "SYSTEM ";
  51.         private const string s_Html = "html";
  52.         private const string s_QuoteSpace = "\" ";
  53.         private const string s_CDataSplit = "]]]]><![CDATA[>";
  54.        
  55.         private const string s_EnLessThan = "&lt;";
  56.         private const string s_EnGreaterThan = "&gt;";
  57.         private const string s_EnAmpersand = "&amp;";
  58.         private const string s_EnQuote = "&quot;";
  59.         private const string s_EnNewLine = "&#xA;";
  60.         private const string s_EnReturn = "&#xD;";
  61.        
  62.         private const string s_EndOfLine = "\r\n";
  63.        
  64.         static char[] s_TextValueFind = new char[] {s_Ampersand, s_GreaterThan, s_LessThan};
  65.         static string[] s_TextValueReplace = new string[] {s_EnAmpersand, s_EnGreaterThan, s_EnLessThan};
  66.        
  67.         static char[] s_XmlAttributeValueFind = new char[] {s_Ampersand, s_GreaterThan, s_LessThan, s_Quote, s_NewLine, s_Return};
  68.         static string[] s_XmlAttributeValueReplace = new string[] {s_EnAmpersand, s_EnGreaterThan, s_EnLessThan, s_EnQuote, s_EnNewLine, s_EnReturn};
  69.        
  70.         // Instance members
  71.         private Processor processor;
  72.         protected Encoding encoding;
  73.         private ArrayList outputCache;
  74.         private bool firstLine = true;
  75.         private bool secondRoot;
  76.        
  77.         // Cached Output propertes:
  78.         private XsltOutput output;
  79.         private bool isHtmlOutput;
  80.         private bool isXmlOutput;
  81.         private Hashtable cdataElements;
  82.         private bool indentOutput;
  83.         private bool outputDoctype;
  84.         private bool outputXmlDecl;
  85.         private bool omitXmlDeclCalled;
  86.        
  87.         // Uri Escaping:
  88.         private byte[] byteBuffer;
  89.         private Encoding utf8Encoding;
  90.        
  91.         XmlCharType xmlCharType = XmlCharType.Instance;
  92.        
  93.         private void CacheOuptutProps(XsltOutput output)
  94.         {
  95.             this.output = output;
  96.             this.isXmlOutput = this.output.Method == XsltOutput.OutputMethod.Xml;
  97.             this.isHtmlOutput = this.output.Method == XsltOutput.OutputMethod.Html;
  98.             this.cdataElements = this.output.CDataElements;
  99.             this.indentOutput = this.output.Indent;
  100.             this.outputDoctype = this.output.DoctypeSystem != null || (this.isHtmlOutput && this.output.DoctypePublic != null);
  101.             this.outputXmlDecl = this.isXmlOutput && !this.output.OmitXmlDeclaration && !this.omitXmlDeclCalled;
  102.         }
  103.        
  104.         //
  105.         // Constructor
  106.         //
  107.         internal SequentialOutput(Processor processor)
  108.         {
  109.             this.processor = processor;
  110.             CacheOuptutProps(processor.Output);
  111.         }
  112.        
  113.         public void OmitXmlDecl()
  114.         {
  115.             this.omitXmlDeclCalled = true;
  116.             this.outputXmlDecl = false;
  117.         }
  118.        
  119.         //
  120.         // Particular outputs
  121.         //
  122.         void WriteStartElement(RecordBuilder record)
  123.         {
  124.             Debug.Assert(record.MainNode.NodeType == XmlNodeType.Element);
  125.             BuilderInfo mainNode = record.MainNode;
  126.             HtmlElementProps htmlProps = null;
  127.             if (this.isHtmlOutput) {
  128.                 if (mainNode.Prefix.Length == 0) {
  129.                     htmlProps = mainNode.htmlProps;
  130.                     if (htmlProps == null && mainNode.search) {
  131.                         htmlProps = HtmlElementProps.GetProps(mainNode.LocalName);
  132.                     }
  133.                     record.Manager.CurrentElementScope.HtmlElementProps = htmlProps;
  134.                     mainNode.IsEmptyTag = false;
  135.                 }
  136.             }
  137.             else if (this.isXmlOutput) {
  138.                 if (mainNode.Depth == 0) {
  139.                     if (secondRoot && (output.DoctypeSystem != null || output.Standalone)) {
  140.                         throw XsltException.Create(Res.Xslt_MultipleRoots);
  141.                     }
  142.                     secondRoot = true;
  143.                 }
  144.             }
  145.            
  146.             if (this.outputDoctype) {
  147.                 WriteDoctype(mainNode);
  148.                 this.outputDoctype = false;
  149.             }
  150.            
  151.             if (this.cdataElements != null && this.cdataElements.Contains(new XmlQualifiedName(mainNode.LocalName, mainNode.NamespaceURI)) && this.isXmlOutput) {
  152.                 record.Manager.CurrentElementScope.ToCData = true;
  153.             }
  154.            
  155.             Indent(record);
  156.             Write(s_LessThan);
  157.             WriteName(mainNode.Prefix, mainNode.LocalName);
  158.            
  159.             WriteAttributes(record.AttributeList, record.AttributeCount, htmlProps);
  160.            
  161.            
  162.             if (mainNode.IsEmptyTag) {
  163.                 Debug.Assert(!this.isHtmlOutput || mainNode.Prefix != null, "Html can't have abreviated elements");
  164.                 Write(s_SlashGreaterThan);
  165.             }
  166.             else {
  167.                 Write(s_GreaterThan);
  168.             }
  169.            
  170.             if (htmlProps != null && htmlProps.Head) {
  171.                 mainNode.Depth++;
  172.                 Indent(record);
  173.                 mainNode.Depth--;
  174.                 Write("<META http-equiv=\"Content-Type\" content=\"");
  175.                 Write(this.output.MediaType);
  176.                 Write("; charset=");
  177.                 Write(this.encoding.WebName);
  178.                 Write("\">");
  179.             }
  180.         }
  181.        
  182.         void WriteTextNode(RecordBuilder record)
  183.         {
  184.             BuilderInfo mainNode = record.MainNode;
  185.             OutputScope scope = record.Manager.CurrentElementScope;
  186.            
  187.             scope.Mixed = true;
  188.            
  189.             if (scope.HtmlElementProps != null && scope.HtmlElementProps.NoEntities) {
  190.                 // script or stile
  191.                 Write(mainNode.Value);
  192.             }
  193.             else if (scope.ToCData) {
  194.                 WriteCDataSection(mainNode.Value);
  195.             }
  196.             else {
  197.                 WriteTextNode(mainNode);
  198.             }
  199.         }
  200.        
  201.         void WriteTextNode(BuilderInfo node)
  202.         {
  203.             for (int i = 0; i < node.TextInfoCount; i++) {
  204.                 string text = node.TextInfo[i];
  205.                 if (text == null) {
  206.                     // disableEscaping marker
  207.                     i++;
  208.                     Debug.Assert(i < node.TextInfoCount, "disableEscaping marker can't be last TextInfo record");
  209.                     Write(node.TextInfo[i]);
  210.                 }
  211.                 else {
  212.                     WriteWithReplace(text, s_TextValueFind, s_TextValueReplace);
  213.                 }
  214.             }
  215.         }
  216.        
  217.         void WriteCDataSection(string value)
  218.         {
  219.             Write(s_CDataBegin);
  220.             WriteCData(value);
  221.             Write(s_CDataEnd);
  222.         }
  223.        
  224.         void WriteDoctype(BuilderInfo mainNode)
  225.         {
  226.             Debug.Assert(this.outputDoctype == true, "It supposed to check this condition before actual call");
  227.             Debug.Assert(this.output.DoctypeSystem != null || (this.isHtmlOutput && this.output.DoctypePublic != null), "We set outputDoctype == true only if");
  228.             Indent(0);
  229.             Write(s_DocType);
  230.             if (this.isXmlOutput) {
  231.                 WriteName(mainNode.Prefix, mainNode.LocalName);
  232.             }
  233.             else {
  234.                 WriteName(string.Empty, s_Html);
  235.             }
  236.             Write(s_Space);
  237.             if (output.DoctypePublic != null) {
  238.                 Write(s_Public);
  239.                 Write(s_Quote);
  240.                 Write(output.DoctypePublic);
  241.                 Write(s_QuoteSpace);
  242.             }
  243.             else {
  244.                 Write(s_System);
  245.             }
  246.             if (output.DoctypeSystem != null) {
  247.                 Write(s_Quote);
  248.                 Write(output.DoctypeSystem);
  249.                 Write(s_Quote);
  250.             }
  251.             Write(s_GreaterThan);
  252.         }
  253.        
  254.         void WriteXmlDeclaration()
  255.         {
  256.             Debug.Assert(this.outputXmlDecl == true, "It supposed to check this condition before actual call");
  257.             Debug.Assert(this.isXmlOutput && !this.output.OmitXmlDeclaration, "We set outputXmlDecl == true only if");
  258.             this.outputXmlDecl = false;
  259.            
  260.             Indent(0);
  261.             Write(s_LessThanQuestion);
  262.             WriteName(string.Empty, "xml");
  263.             Write(s_VersionAll);
  264.             if (this.encoding != null) {
  265.                 Write(s_EncodingStart);
  266.                 Write(this.encoding.WebName);
  267.                 Write(s_Quote);
  268.             }
  269.             if (output.HasStandalone) {
  270.                 Write(s_Standalone);
  271.                 Write(output.Standalone ? Keywords.s_Yes : Keywords.s_No);
  272.                 Write(s_Quote);
  273.             }
  274.             Write(s_QuestionGreaterThan);
  275.         }
  276.        
  277.         void WriteProcessingInstruction(RecordBuilder record)
  278.         {
  279.             Indent(record);
  280.             WriteProcessingInstruction(record.MainNode);
  281.         }
  282.        
  283.         void WriteProcessingInstruction(BuilderInfo node)
  284.         {
  285.             Write(s_LessThanQuestion);
  286.             WriteName(node.Prefix, node.LocalName);
  287.             Write(s_Space);
  288.             Write(node.Value);
  289.            
  290.             if (this.isHtmlOutput) {
  291.                 Write(s_GreaterThan);
  292.             }
  293.             else {
  294.                 Write(s_QuestionGreaterThan);
  295.             }
  296.         }
  297.        
  298.         void WriteEndElement(RecordBuilder record)
  299.         {
  300.             BuilderInfo node = record.MainNode;
  301.             HtmlElementProps htmlProps = record.Manager.CurrentElementScope.HtmlElementProps;
  302.            
  303.             if (htmlProps != null && htmlProps.Empty) {
  304.                 return;
  305.             }
  306.            
  307.             Indent(record);
  308.             Write(s_LessThanSlash);
  309.             WriteName(record.MainNode.Prefix, record.MainNode.LocalName);
  310.             Write(s_GreaterThan);
  311.         }
  312.        
  313.         //
  314.         // RecordOutput interface method implementation
  315.         //
  316.        
  317.         public Processor.OutputResult RecordDone(RecordBuilder record)
  318.         {
  319.             if (output.Method == XsltOutput.OutputMethod.Unknown) {
  320.                 if (!DecideDefaultOutput(record.MainNode)) {
  321.                     CacheRecord(record);
  322.                 }
  323.                 else {
  324.                     OutputCachedRecords();
  325.                     OutputRecord(record);
  326.                 }
  327.             }
  328.             else {
  329.                 OutputRecord(record);
  330.             }
  331.            
  332.             record.Reset();
  333.             return Processor.OutputResult.Continue;
  334.         }
  335.        
  336.         public void TheEnd()
  337.         {
  338.             OutputCachedRecords();
  339.             Close();
  340.         }
  341.        
  342.         private bool DecideDefaultOutput(BuilderInfo node)
  343.         {
  344.             XsltOutput.OutputMethod method = XsltOutput.OutputMethod.Xml;
  345.             switch (node.NodeType) {
  346.                 case XmlNodeType.Element:
  347.                     if (node.NamespaceURI.Length == 0 && String.Compare(Keywords.s_Html, node.LocalName, StringComparison.OrdinalIgnoreCase) == 0) {
  348.                         method = XsltOutput.OutputMethod.Html;
  349.                     }
  350.                     break;
  351.                 case XmlNodeType.Text:
  352.                 case XmlNodeType.Whitespace:
  353.                 case XmlNodeType.SignificantWhitespace:
  354.                     if (xmlCharType.IsOnlyWhitespace(node.Value)) {
  355.                         return false;
  356.                     }
  357.                     method = XsltOutput.OutputMethod.Xml;
  358.                     break;
  359.                 default:
  360.                     return false;
  361.             }
  362.             if (this.processor.SetDefaultOutput(method)) {
  363.                 CacheOuptutProps(processor.Output);
  364.             }
  365.             return true;
  366.         }
  367.        
  368.         private void CacheRecord(RecordBuilder record)
  369.         {
  370.             if (this.outputCache == null) {
  371.                 this.outputCache = new ArrayList();
  372.             }
  373.            
  374.             this.outputCache.Add(record.MainNode.Clone());
  375.         }
  376.        
  377.         private void OutputCachedRecords()
  378.         {
  379.             if (this.outputCache == null) {
  380.                 return;
  381.             }
  382.            
  383.             for (int record = 0; record < this.outputCache.Count; record++) {
  384.                 Debug.Assert(this.outputCache[record] is BuilderInfo);
  385.                 BuilderInfo info = (BuilderInfo)this.outputCache[record];
  386.                
  387.                 OutputRecord(info);
  388.             }
  389.            
  390.             this.outputCache = null;
  391.         }
  392.        
  393.         private void OutputRecord(RecordBuilder record)
  394.         {
  395.             BuilderInfo mainNode = record.MainNode;
  396.            
  397.             if (this.outputXmlDecl) {
  398.                 WriteXmlDeclaration();
  399.             }
  400.            
  401.             switch (mainNode.NodeType) {
  402.                 case XmlNodeType.Element:
  403.                     WriteStartElement(record);
  404.                     break;
  405.                 case XmlNodeType.Text:
  406.                 case XmlNodeType.Whitespace:
  407.                 case XmlNodeType.SignificantWhitespace:
  408.                     WriteTextNode(record);
  409.                     break;
  410.                 case XmlNodeType.CDATA:
  411.                     Debug.Fail("Should never get here");
  412.                     break;
  413.                 case XmlNodeType.EntityReference:
  414.                     Write(s_Ampersand);
  415.                     WriteName(mainNode.Prefix, mainNode.LocalName);
  416.                     Write(s_Semicolon);
  417.                     break;
  418.                 case XmlNodeType.ProcessingInstruction:
  419.                     WriteProcessingInstruction(record);
  420.                     break;
  421.                 case XmlNodeType.Comment:
  422.                     Indent(record);
  423.                     Write(s_CommentBegin);
  424.                     Write(mainNode.Value);
  425.                     Write(s_CommentEnd);
  426.                     break;
  427.                 case XmlNodeType.Document:
  428.                     break;
  429.                 case XmlNodeType.DocumentType:
  430.                     Write(mainNode.Value);
  431.                     break;
  432.                 case XmlNodeType.EndElement:
  433.                     WriteEndElement(record);
  434.                     break;
  435.                 default:
  436.                     break;
  437.             }
  438.         }
  439.        
  440.         private void OutputRecord(BuilderInfo node)
  441.         {
  442.             if (this.outputXmlDecl) {
  443.                 WriteXmlDeclaration();
  444.             }
  445.            
  446.             Indent(0);
  447.             // we can have only top level stuff here
  448.             switch (node.NodeType) {
  449.                 case XmlNodeType.Element:
  450.                     Debug.Fail("Should never get here");
  451.                     break;
  452.                 case XmlNodeType.Text:
  453.                 case XmlNodeType.Whitespace:
  454.                 case XmlNodeType.SignificantWhitespace:
  455.                     WriteTextNode(node);
  456.                     break;
  457.                 case XmlNodeType.CDATA:
  458.                     Debug.Fail("Should never get here");
  459.                     break;
  460.                 case XmlNodeType.EntityReference:
  461.                     Write(s_Ampersand);
  462.                     WriteName(node.Prefix, node.LocalName);
  463.                     Write(s_Semicolon);
  464.                     break;
  465.                 case XmlNodeType.ProcessingInstruction:
  466.                     WriteProcessingInstruction(node);
  467.                     break;
  468.                 case XmlNodeType.Comment:
  469.                     Write(s_CommentBegin);
  470.                     Write(node.Value);
  471.                     Write(s_CommentEnd);
  472.                     break;
  473.                 case XmlNodeType.Document:
  474.                     break;
  475.                 case XmlNodeType.DocumentType:
  476.                     Write(node.Value);
  477.                     break;
  478.                 case XmlNodeType.EndElement:
  479.                     Debug.Fail("Should never get here");
  480.                     break;
  481.                 default:
  482.                     break;
  483.             }
  484.         }
  485.        
  486.         //
  487.         // Internal helpers
  488.         //
  489.        
  490.         private void WriteName(string prefix, string name)
  491.         {
  492.             if (prefix != null && prefix.Length > 0) {
  493.                 Write(prefix);
  494.                 if (name != null && name.Length > 0) {
  495.                     Write(s_Colon);
  496.                 }
  497.                 else {
  498.                     return;
  499.                 }
  500.             }
  501.             Write(name);
  502.         }
  503.        
  504.         private void WriteXmlAttributeValue(string value)
  505.         {
  506.             Debug.Assert(value != null);
  507.             WriteWithReplace(value, s_XmlAttributeValueFind, s_XmlAttributeValueReplace);
  508.         }
  509.        
  510.         private void WriteHtmlAttributeValue(string value)
  511.         {
  512.             Debug.Assert(value != null);
  513.            
  514.             int length = value.Length;
  515.             int i = 0;
  516.             while (i < length) {
  517.                 char ch = value[i];
  518.                 i++;
  519.                 switch (ch) {
  520.                     case '&':
  521.                         if (i != length && value[i] == '{') {
  522.                             // &{ hasn't to be encoded in HTML output.
  523.                             Write(ch);
  524.                         }
  525.                         else {
  526.                             Write(s_EnAmpersand);
  527.                         }
  528.                         break;
  529.                     case '"':
  530.                         Write(s_EnQuote);
  531.                         break;
  532.                     default:
  533.                         Write(ch);
  534.                         break;
  535.                 }
  536.             }
  537.         }
  538.        
  539.         private void WriteHtmlUri(string value)
  540.         {
  541.             Debug.Assert(value != null);
  542.             Debug.Assert(this.isHtmlOutput);
  543.            
  544.             int length = value.Length;
  545.             int i = 0;
  546.             while (i < length) {
  547.                 char ch = value[i];
  548.                 i++;
  549.                 switch (ch) {
  550.                     case '&':
  551.                         if (i != length && value[i] == '{') {
  552.                             // &{ hasn't to be encoded in HTML output.
  553.                             Write(ch);
  554.                         }
  555.                         else {
  556.                             Write(s_EnAmpersand);
  557.                         }
  558.                         break;
  559.                     case '"':
  560.                         Write(s_EnQuote);
  561.                         break;
  562.                     case '\n':
  563.                         Write(s_EnNewLine);
  564.                         break;
  565.                     case '\r':
  566.                         Write(s_EnReturn);
  567.                         break;
  568.                     default:
  569.                         if (127 < ch) {
  570.                             if (this.utf8Encoding == null) {
  571.                                 this.utf8Encoding = Encoding.UTF8;
  572.                                 this.byteBuffer = new byte[utf8Encoding.GetMaxByteCount(1)];
  573.                             }
  574.                             int bytes = this.utf8Encoding.GetBytes(value, i - 1, 1, this.byteBuffer, 0);
  575.                             for (int j = 0; j < bytes; j++) {
  576.                                 Write("%");
  577.                                 Write(((uint)this.byteBuffer[j]).ToString("X2", CultureInfo.InvariantCulture));
  578.                             }
  579.                         }
  580.                         else {
  581.                             Write(ch);
  582.                         }
  583.                         break;
  584.                 }
  585.             }
  586.         }
  587.        
  588.         private void WriteWithReplace(string value, char[] find, string[] replace)
  589.         {
  590.             Debug.Assert(value != null);
  591.             Debug.Assert(find.Length == replace.Length);
  592.            
  593.             int length = value.Length;
  594.             int pos = 0;
  595.            
  596.             while (pos < length) {
  597.                 int newPos = value.IndexOfAny(find, pos);
  598.                 if (newPos == -1) {
  599.                     break;
  600.                     // not found;
  601.                 }
  602.                 // output clean leading part of the string
  603.                 while (pos < newPos) {
  604.                     Write(value[pos]);
  605.                     pos++;
  606.                 }
  607.                 // output replacement
  608.                 char badChar = value[pos];
  609.                 int i;
  610.                 for (i = find.Length - 1; 0 <= i; i--) {
  611.                     if (find[i] == badChar) {
  612.                         Write(replace[i]);
  613.                         break;
  614.                     }
  615.                 }
  616.                 Debug.Assert(0 <= i, "find char wasn't realy find");
  617.                 pos++;
  618.             }
  619.            
  620.             // output rest of the string
  621.             if (pos == 0) {
  622.                 Write(value);
  623.             }
  624.             else {
  625.                 while (pos < length) {
  626.                     Write(value[pos]);
  627.                     pos++;
  628.                 }
  629.             }
  630.         }
  631.        
  632.         private void WriteCData(string value)
  633.         {
  634.             Debug.Assert(value != null);
  635.             Write(value.Replace(s_CDataEnd, s_CDataSplit));
  636.         }
  637.        
  638.         private void WriteAttributes(ArrayList list, int count, HtmlElementProps htmlElementsProps)
  639.         {
  640.             Debug.Assert(count <= list.Count);
  641.             for (int attrib = 0; attrib < count; attrib++) {
  642.                 Debug.Assert(list[attrib] is BuilderInfo);
  643.                 BuilderInfo attribute = (BuilderInfo)list[attrib];
  644.                 string attrValue = attribute.Value;
  645.                 bool abr = false;
  646.                 bool uri = false;
  647.                 {
  648.                     if (htmlElementsProps != null && attribute.Prefix.Length == 0) {
  649.                         HtmlAttributeProps htmlAttrProps = attribute.htmlAttrProps;
  650.                         if (htmlAttrProps == null && attribute.search) {
  651.                             htmlAttrProps = HtmlAttributeProps.GetProps(attribute.LocalName);
  652.                         }
  653.                         if (htmlAttrProps != null) {
  654.                             abr = htmlElementsProps.AbrParent && htmlAttrProps.Abr;
  655.                             uri = htmlElementsProps.UriParent && (htmlAttrProps.Uri || htmlElementsProps.NameParent && htmlAttrProps.Name);
  656.                         }
  657.                     }
  658.                 }
  659.                 Write(s_Space);
  660.                 WriteName(attribute.Prefix, attribute.LocalName);
  661.                 if (abr && 0 == string.Compare(attribute.LocalName, attrValue, StringComparison.OrdinalIgnoreCase)) {
  662.                     // Since the name of the attribute = the value of the attribute,
  663.                     // this is a boolean attribute whose value should be suppressed
  664.                     continue;
  665.                 }
  666.                 Write(s_EqualQuote);
  667.                 if (uri) {
  668.                     WriteHtmlUri(attrValue);
  669.                 }
  670.                 else if (this.isHtmlOutput) {
  671.                     WriteHtmlAttributeValue(attrValue);
  672.                 }
  673.                 else {
  674.                     WriteXmlAttributeValue(attrValue);
  675.                 }
  676.                 Write(s_Quote);
  677.             }
  678.         }
  679.        
  680.         void Indent(RecordBuilder record)
  681.         {
  682.             if (!record.Manager.CurrentElementScope.Mixed) {
  683.                 Indent(record.MainNode.Depth);
  684.             }
  685.         }
  686.        
  687.         void Indent(int depth)
  688.         {
  689.             if (this.firstLine) {
  690.                 if (this.indentOutput) {
  691.                     this.firstLine = false;
  692.                 }
  693.                 return;
  694.                 // preven leading CRLF
  695.             }
  696.             Write(s_EndOfLine);
  697.             for (int i = 2 * depth; 0 < i; i--) {
  698.                 Write(" ");
  699.             }
  700.         }
  701.        
  702.         //
  703.         // Abstract methods
  704.         internal abstract void Write(char outputChar);
  705.         internal abstract void Write(string outputText);
  706.         internal abstract void Close();
  707.     }
  708. }

Developer Fusion