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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlTextWriter.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Text;
  20. using System.Diagnostics;
  21. using System.Globalization;
  22. namespace System.Xml
  23. {
  24.    
  25.     // Specifies formatting options for XmlTextWriter.
  26.     public enum Formatting
  27.     {
  28.         // No special formatting is done (this is the default).
  29.         None,
  30.        
  31.         //This option causes child elements to be indented using the Indentation and IndentChar properties.
  32.         // It only indents Element Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-element-content)
  33.         // and not Mixed Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-mixed-content)
  34.         // according to the XML 1.0 definitions of these terms.
  35.         Indented
  36.     }
  37.    
  38.     // Represents a writer that provides fast non-cached forward-only way of generating XML streams
  39.     // containing XML documents that conform to the W3CExtensible Markup Language (XML) 1.0 specification
  40.     // and the Namespaces in XML specification.
  41.     public class XmlTextWriter : XmlWriter
  42.     {
  43.         //
  44.         // Private types
  45.         //
  46.         enum NamespaceState
  47.         {
  48.             Uninitialized,
  49.             NotDeclaredButInScope,
  50.             DeclaredButNotWrittenOut,
  51.             DeclaredAndWrittenOut
  52.         }
  53.        
  54.         struct TagInfo
  55.         {
  56.             internal string name;
  57.             internal string prefix;
  58.             internal string defaultNs;
  59.             internal NamespaceState defaultNsState;
  60.             internal XmlSpace xmlSpace;
  61.             internal string xmlLang;
  62.             internal int prevNsTop;
  63.             internal int prefixCount;
  64.             internal bool mixed;
  65.             // whether to pretty print the contents of this element.
  66.             internal void Init(int nsTop)
  67.             {
  68.                 name = null;
  69.                 defaultNs = String.Empty;
  70.                 defaultNsState = NamespaceState.Uninitialized;
  71.                 xmlSpace = XmlSpace.None;
  72.                 xmlLang = null;
  73.                 prevNsTop = nsTop;
  74.                 prefixCount = 0;
  75.                 mixed = false;
  76.             }
  77.         }
  78.        
  79.         struct Namespace
  80.         {
  81.             internal string prefix;
  82.             internal string ns;
  83.             internal bool declared;
  84.             internal int prevNsIndex;
  85.            
  86.             internal void Set(string prefix, string ns, bool declared)
  87.             {
  88.                 this.prefix = prefix;
  89.                 this.ns = ns;
  90.                 this.declared = declared;
  91.                 this.prevNsIndex = -1;
  92.             }
  93.         }
  94.        
  95.         enum SpecialAttr
  96.         {
  97.             None,
  98.             XmlSpace,
  99.             XmlLang,
  100.             XmlNs
  101.         }
  102.        
  103.         // State machine is working through autocomplete
  104.         private enum State
  105.         {
  106.             Start,
  107.             Prolog,
  108.             PostDTD,
  109.             Element,
  110.             Attribute,
  111.             Content,
  112.             AttrOnly,
  113.             Epilog,
  114.             Error,
  115.             Closed
  116.         }
  117.        
  118.         private enum Token
  119.         {
  120.             PI,
  121.             Doctype,
  122.             Comment,
  123.             CData,
  124.             StartElement,
  125.             EndElement,
  126.             LongEndElement,
  127.             StartAttribute,
  128.             EndAttribute,
  129.             Content,
  130.             Base64,
  131.             RawData,
  132.             Whitespace,
  133.             Empty
  134.         }
  135.        
  136.         //
  137.         // Fields
  138.         //
  139.         // output
  140.         TextWriter textWriter;
  141.         XmlTextEncoder xmlEncoder;
  142.         Encoding encoding;
  143.        
  144.         // formatting
  145.         Formatting formatting;
  146.         bool indented;
  147.         // perf - faster to check a boolean.
  148.         int indentation;
  149.         char indentChar;
  150.        
  151.         // element stack
  152.         TagInfo[] stack;
  153.         int top;
  154.        
  155.         // state machine for AutoComplete
  156.         State[] stateTable;
  157.         State currentState;
  158.         Token lastToken;
  159.        
  160.         // Base64 content
  161.         XmlTextWriterBase64Encoder base64Encoder;
  162.        
  163.         // misc
  164.         char quoteChar;
  165.         char curQuoteChar;
  166.         bool namespaces;
  167.         SpecialAttr specialAttr;
  168.         string prefixForXmlNs;
  169.         bool flush;
  170.        
  171.         // namespaces
  172.         Namespace[] nsStack;
  173.         int nsTop;
  174.         Dictionary<string, int> nsHashtable;
  175.         bool useNsHashtable;
  176.        
  177.         // char types
  178.         XmlCharType xmlCharType = XmlCharType.Instance;
  179.        
  180.         //
  181.         // Constants and constant tables
  182.         //
  183.         const int NamespaceStackInitialSize = 8;
  184.         #if DEBUG
  185.         const int MaxNamespacesWalkCount = 3;
  186.         #else
  187.         const int MaxNamespacesWalkCount = 16;
  188.         #endif
  189.        
  190.         static string[] stateName = {"Start", "Prolog", "PostDTD", "Element", "Attribute", "Content", "AttrOnly", "Epilog", "Error", "Closed"
  191.         };
  192.        
  193.         static string[] tokenName = {"PI", "Doctype", "Comment", "CData", "StartElement", "EndElement", "LongEndElement", "StartAttribute", "EndAttribute", "Content",
  194.         "Base64", "RawData", "Whitespace", "Empty"};
  195.        
  196.         static readonly State[] stateTableDefault = {State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, State.PostDTD, State.PostDTD,
  197.         State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Prolog, State.Prolog, State.PostDTD, State.Content,
  198.         State.Content, State.Content, State.Error, State.Epilog, State.Content, State.Content, State.Error, State.Content, State.Content, State.Content,
  199.         State.Error, State.Epilog, State.Element, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Element,
  200.         State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, State.Error, State.Error,
  201.         State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, State.AttrOnly, State.Error, State.Error, State.Attribute,
  202.         State.Attribute, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Element, State.Error,
  203.         State.Epilog, State.Error, State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog,
  204.         State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, State.Prolog, State.Prolog,
  205.         State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, State.Prolog, State.Prolog, State.PostDTD, State.Content,
  206.             // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog
  207.             //
  208.             /* Token.PI            */            /* Token.Doctype        */            /* Token.Comment        */            /* Token.CData          */            /* Token.StartElement  */            /* Token.EndElement    */            /* Token.LongEndElement */            /* Token.StartAttribute */            /* Token.EndAttribute  */            /* Token.Content        */            /* Token.Base64        */            /* Token.RawData        */            /* Token.Whitespace    */        State.Attribute, State.Content, State.Attribute, State.Epilog};
  209.        
  210.         static readonly State[] stateTableDocument = {State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, State.Error, State.PostDTD,
  211.         State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Prolog, State.PostDTD, State.Content,
  212.         State.Content, State.Content, State.Error, State.Epilog, State.Error, State.Error, State.Error, State.Content, State.Content, State.Content,
  213.         State.Error, State.Error, State.Error, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Error,
  214.         State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, State.Error, State.Error,
  215.         State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, State.Error, State.Error, State.Error, State.Attribute,
  216.         State.Attribute, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Element, State.Error,
  217.         State.Error, State.Error, State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error,
  218.         State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error, State.Error, State.Prolog,
  219.         State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog, State.Error, State.Prolog, State.PostDTD, State.Content,
  220.             // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog
  221.             //
  222.             /* Token.PI            */            /* Token.Doctype        */            /* Token.Comment        */            /* Token.CData          */            /* Token.StartElement  */            /* Token.EndElement    */            /* Token.LongEndElement */            /* Token.StartAttribute */            /* Token.EndAttribute  */            /* Token.Content        */            /* Token.Base64        */            /* Token.RawData        */            /* Token.Whitespace    */        State.Attribute, State.Content, State.Error, State.Epilog};
  223.        
  224.         //
  225.         // Constructors
  226.         //
  227.         internal XmlTextWriter()
  228.         {
  229.             namespaces = true;
  230.             formatting = Formatting.None;
  231.             indentation = 2;
  232.             indentChar = ' ';
  233.             // namespaces
  234.             nsStack = new Namespace[NamespaceStackInitialSize];
  235.             nsTop = -1;
  236.             // element stack
  237.             stack = new TagInfo[10];
  238.             top = 0;
  239.             // 0 is an empty sentanial element
  240.             stack[top].Init(-1);
  241.             quoteChar = '"';
  242.            
  243.             stateTable = stateTableDefault;
  244.             currentState = State.Start;
  245.             lastToken = Token.Empty;
  246.         }
  247.        
  248.         // Creates an instance of the XmlTextWriter class using the specified stream.
  249.         public XmlTextWriter(Stream w, Encoding encoding) : this()
  250.         {
  251.             this.encoding = encoding;
  252.             if (encoding != null)
  253.                 textWriter = new StreamWriter(w, encoding);
  254.             else
  255.                 textWriter = new StreamWriter(w);
  256.             xmlEncoder = new XmlTextEncoder(textWriter);
  257.             xmlEncoder.QuoteChar = this.quoteChar;
  258.         }
  259.        
  260.         // Creates an instance of the XmlTextWriter class using the specified file.
  261.         public XmlTextWriter(string filename, Encoding encoding) : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), encoding)
  262.         {
  263.         }
  264.        
  265.         // Creates an instance of the XmlTextWriter class using the specified TextWriter.
  266.         public XmlTextWriter(TextWriter w) : this()
  267.         {
  268.             textWriter = w;
  269.            
  270.             encoding = w.Encoding;
  271.             xmlEncoder = new XmlTextEncoder(w);
  272.             xmlEncoder.QuoteChar = this.quoteChar;
  273.         }
  274.        
  275.         //
  276.         // XmlTextWriter properties
  277.         //
  278.         // Gets the XmlTextWriter base stream.
  279.         public Stream BaseStream {
  280.             get {
  281.                 StreamWriter streamWriter = textWriter as StreamWriter;
  282.                 return (streamWriter == null ? null : streamWriter.BaseStream);
  283.             }
  284.         }
  285.        
  286.         // Gets or sets a value indicating whether to do namespace support.
  287.         public bool Namespaces {
  288.             get { return this.namespaces; }
  289.             set {
  290.                 if (this.currentState != State.Start)
  291.                     throw new InvalidOperationException(Res.GetString(Res.Xml_NotInWriteState));
  292.                
  293.                 this.namespaces = value;
  294.             }
  295.         }
  296.        
  297.         // Indicates how the output is formatted.
  298.         public Formatting Formatting {
  299.             get { return this.formatting; }
  300.             set {
  301.                 this.formatting = value;
  302.                 this.indented = value == Formatting.Indented;
  303.             }
  304.         }
  305.        
  306.         // Gets or sets how many IndentChars to write for each level in the hierarchy when Formatting is set to "Indented".
  307.         public int Indentation {
  308.             get { return this.indentation; }
  309.             set {
  310.                 if (value < 0)
  311.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidIndentation));
  312.                 this.indentation = value;
  313.             }
  314.         }
  315.        
  316.         // Gets or sets which character to use for indenting when Formatting is set to "Indented".
  317.         public char IndentChar {
  318.             get { return this.indentChar; }
  319.             set { this.indentChar = value; }
  320.         }
  321.        
  322.         // Gets or sets which character to use to quote attribute values.
  323.         public char QuoteChar {
  324.             get { return this.quoteChar; }
  325.             set {
  326.                 if (value != '"' && value != '\'') {
  327.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidQuote));
  328.                 }
  329.                 this.quoteChar = value;
  330.                 this.xmlEncoder.QuoteChar = value;
  331.             }
  332.         }
  333.        
  334.         //
  335.         // XmlWriter implementation
  336.         //
  337.         // Writes out the XML declaration with the version "1.0".
  338.         public override void WriteStartDocument()
  339.         {
  340.             StartDocument(-1);
  341.         }
  342.        
  343.         // Writes out the XML declaration with the version "1.0" and the standalone attribute.
  344.         public override void WriteStartDocument(bool standalone)
  345.         {
  346.             StartDocument(standalone ? 1 : 0);
  347.         }
  348.        
  349.         // Closes any open elements or attributes and puts the writer back in the Start state.
  350.         public override void WriteEndDocument()
  351.         {
  352.             try {
  353.                 AutoCompleteAll();
  354.                 if (this.currentState != State.Epilog) {
  355.                     throw new ArgumentException(Res.GetString(Res.Xml_NoRoot));
  356.                 }
  357.                 this.stateTable = stateTableDefault;
  358.                 this.currentState = State.Start;
  359.                 this.lastToken = Token.Empty;
  360.             }
  361.             catch {
  362.                 currentState = State.Error;
  363.                 throw;
  364.             }
  365.         }
  366.        
  367.         // Writes out the DOCTYPE declaration with the specified name and optional attributes.
  368.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  369.         {
  370.             try {
  371.                 ValidateName(name, false);
  372.                
  373.                 AutoComplete(Token.Doctype);
  374.                 textWriter.Write("<!DOCTYPE ");
  375.                 textWriter.Write(name);
  376.                 if (pubid != null) {
  377.                     textWriter.Write(" PUBLIC " + quoteChar);
  378.                     textWriter.Write(pubid);
  379.                     textWriter.Write(quoteChar + " " + quoteChar);
  380.                     textWriter.Write(sysid);
  381.                     textWriter.Write(quoteChar);
  382.                 }
  383.                 else if (sysid != null) {
  384.                     textWriter.Write(" SYSTEM " + quoteChar);
  385.                     textWriter.Write(sysid);
  386.                     textWriter.Write(quoteChar);
  387.                 }
  388.                 if (subset != null) {
  389.                     textWriter.Write("[");
  390.                     textWriter.Write(subset);
  391.                     textWriter.Write("]");
  392.                 }
  393.                 textWriter.Write('>');
  394.             }
  395.             catch {
  396.                 currentState = State.Error;
  397.                 throw;
  398.             }
  399.         }
  400.        
  401.         // Writes out the specified start tag and associates it with the given namespace and prefix.
  402.         public override void WriteStartElement(string prefix, string localName, string ns)
  403.         {
  404.             try {
  405.                 AutoComplete(Token.StartElement);
  406.                 PushStack();
  407.                 textWriter.Write('<');
  408.                
  409.                 if (this.namespaces) {
  410.                     // Propagate default namespace and mix model down the stack.
  411.                     stack[top].defaultNs = stack[top - 1].defaultNs;
  412.                     if (stack[top - 1].defaultNsState != NamespaceState.Uninitialized)
  413.                         stack[top].defaultNsState = NamespaceState.NotDeclaredButInScope;
  414.                     stack[top].mixed = stack[top - 1].mixed;
  415.                     if (ns == null) {
  416.                         // use defined prefix
  417.                         if (prefix != null && prefix.Length != 0 && (LookupNamespace(prefix) == -1)) {
  418.                             throw new ArgumentException(Res.GetString(Res.Xml_UndefPrefix));
  419.                         }
  420.                     }
  421.                     else {
  422.                         if (prefix == null) {
  423.                             string definedPrefix = FindPrefix(ns);
  424.                             if (definedPrefix != null) {
  425.                                 prefix = definedPrefix;
  426.                             }
  427.                             else {
  428.                                 PushNamespace(null, ns, false);
  429.                                 // new default
  430.                             }
  431.                         }
  432.                         else if (prefix.Length == 0) {
  433.                             PushNamespace(null, ns, false);
  434.                             // new default
  435.                         }
  436.                         else {
  437.                             if (ns.Length == 0) {
  438.                                 prefix = null;
  439.                             }
  440.                             VerifyPrefixXml(prefix, ns);
  441.                             PushNamespace(prefix, ns, false);
  442.                             // define
  443.                         }
  444.                     }
  445.                     stack[top].prefix = null;
  446.                     if (prefix != null && prefix.Length != 0) {
  447.                         stack[top].prefix = prefix;
  448.                         textWriter.Write(prefix);
  449.                         textWriter.Write(':');
  450.                     }
  451.                 }
  452.                 else {
  453.                     if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) {
  454.                         throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces));
  455.                     }
  456.                 }
  457.                 stack[top].name = localName;
  458.                 textWriter.Write(localName);
  459.             }
  460.             catch {
  461.                 currentState = State.Error;
  462.                 throw;
  463.             }
  464.         }
  465.        
  466.         // Closes one element and pops the corresponding namespace scope.
  467.         public override void WriteEndElement()
  468.         {
  469.             InternalWriteEndElement(false);
  470.         }
  471.        
  472.         // Closes one element and pops the corresponding namespace scope.
  473.         public override void WriteFullEndElement()
  474.         {
  475.             InternalWriteEndElement(true);
  476.         }
  477.        
  478.         // Writes the start of an attribute.
  479.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  480.         {
  481.             try {
  482.                 AutoComplete(Token.StartAttribute);
  483.                
  484.                 this.specialAttr = SpecialAttr.None;
  485.                 if (this.namespaces) {
  486.                    
  487.                     if (prefix != null && prefix.Length == 0) {
  488.                         prefix = null;
  489.                     }
  490.                    
  491.                     if (ns == XmlReservedNs.NsXmlNs && prefix == null && localName != "xmlns") {
  492.                         prefix = "xmlns";
  493.                     }
  494.                    
  495.                     if (prefix == "xml") {
  496.                         if (localName == "lang") {
  497.                             this.specialAttr = SpecialAttr.XmlLang;
  498.                         }
  499.                         else if (localName == "space") {
  500.                             this.specialAttr = SpecialAttr.XmlSpace;
  501.                         }
  502.                         /* bug54408. to be fwd compatible we need to treat xml prefix as reserved
  503.                         and not really insist on a specific value. Who knows in the future it
  504.                         might be OK to say xml:blabla
  505.                         else {
  506.                             throw new ArgumentException(Res.GetString(Res.Xml_InvalidPrefix));
  507.                         }*/                       
  508.                     }
  509.                     else if (prefix == "xmlns") {
  510.                        
  511.                         if (XmlReservedNs.NsXmlNs != ns && ns != null) {
  512.                             throw new ArgumentException(Res.GetString(Res.Xml_XmlnsBelongsToReservedNs));
  513.                         }
  514.                         if (localName == null || localName.Length == 0) {
  515.                             localName = prefix;
  516.                             prefix = null;
  517.                             this.prefixForXmlNs = null;
  518.                         }
  519.                         else {
  520.                             this.prefixForXmlNs = localName;
  521.                         }
  522.                         this.specialAttr = SpecialAttr.XmlNs;
  523.                     }
  524.                     else if (prefix == null && localName == "xmlns") {
  525.                         if (XmlReservedNs.NsXmlNs != ns && ns != null) {
  526.                             // add the below line back in when DOM is fixed
  527.                             throw new ArgumentException(Res.GetString(Res.Xml_XmlnsBelongsToReservedNs));
  528.                         }
  529.                         this.specialAttr = SpecialAttr.XmlNs;
  530.                         this.prefixForXmlNs = null;
  531.                     }
  532.                     else {
  533.                         if (ns == null) {
  534.                             // use defined prefix
  535.                             if (prefix != null && (LookupNamespace(prefix) == -1)) {
  536.                                 throw new ArgumentException(Res.GetString(Res.Xml_UndefPrefix));
  537.                             }
  538.                         }
  539.                         else if (ns.Length == 0) {
  540.                             // empty namespace require null prefix
  541.                             prefix = string.Empty;
  542.                         }
  543.                         else {
  544.                             // ns.Length != 0
  545.                             VerifyPrefixXml(prefix, ns);
  546.                             if (prefix != null && LookupNamespaceInCurrentScope(prefix) != -1) {
  547.                                 prefix = null;
  548.                             }
  549.                             // Now verify prefix validity
  550.                             string definedPrefix = FindPrefix(ns);
  551.                             if (definedPrefix != null && (prefix == null || prefix == definedPrefix)) {
  552.                                 prefix = definedPrefix;
  553.                             }
  554.                             else {
  555.                                 if (prefix == null) {
  556.                                     prefix = GeneratePrefix();
  557.                                     // need a prefix if
  558.                                 }
  559.                                 PushNamespace(prefix, ns, false);
  560.                             }
  561.                         }
  562.                     }
  563.                     if (prefix != null && prefix.Length != 0) {
  564.                         textWriter.Write(prefix);
  565.                         textWriter.Write(':');
  566.                     }
  567.                 }
  568.                 else {
  569.                     if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) {
  570.                         throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces));
  571.                     }
  572.                     if (localName == "xml:lang") {
  573.                         this.specialAttr = SpecialAttr.XmlLang;
  574.                     }
  575.                     else if (localName == "xml:space") {
  576.                         this.specialAttr = SpecialAttr.XmlSpace;
  577.                     }
  578.                 }
  579.                 xmlEncoder.StartAttribute(this.specialAttr != SpecialAttr.None);
  580.                
  581.                 textWriter.Write(localName);
  582.                 textWriter.Write('=');
  583.                 if (this.curQuoteChar != this.quoteChar) {
  584.                     this.curQuoteChar = this.quoteChar;
  585.                     xmlEncoder.QuoteChar = this.quoteChar;
  586.                 }
  587.                 textWriter.Write(this.curQuoteChar);
  588.             }
  589.             catch {
  590.                 currentState = State.Error;
  591.                 throw;
  592.             }
  593.         }
  594.        
  595.         // Closes the attribute opened by WriteStartAttribute.
  596.         public override void WriteEndAttribute()
  597.         {
  598.             try {
  599.                 AutoComplete(Token.EndAttribute);
  600.             }
  601.             catch {
  602.                 currentState = State.Error;
  603.                 throw;
  604.             }
  605.         }
  606.        
  607.         // Writes out a &lt;![CDATA[...]]&gt; block containing the specified text.
  608.         public override void WriteCData(string text)
  609.         {
  610.             try {
  611.                 AutoComplete(Token.CData);
  612.                 if (null != text && text.IndexOf("]]>", StringComparison.Ordinal) >= 0) {
  613.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidCDataChars));
  614.                 }
  615.                 textWriter.Write("<![CDATA[");
  616.                
  617.                 if (null != text) {
  618.                     xmlEncoder.WriteRawWithSurrogateChecking(text);
  619.                 }
  620.                 textWriter.Write("]]>");
  621.             }
  622.             catch {
  623.                 currentState = State.Error;
  624.                 throw;
  625.             }
  626.         }
  627.        
  628.         // Writes out a comment <!--...--> containing the specified text.
  629.         public override void WriteComment(string text)
  630.         {
  631.             try {
  632.                 if (null != text && (text.IndexOf("--", StringComparison.Ordinal) >= 0 || (text.Length != 0 && text[text.Length - 1] == '-'))) {
  633.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidCommentChars));
  634.                 }
  635.                 AutoComplete(Token.Comment);
  636.                 textWriter.Write("<!--");
  637.                 if (null != text) {
  638.                     xmlEncoder.WriteRawWithSurrogateChecking(text);
  639.                 }
  640.                 textWriter.Write("-->");
  641.             }
  642.             catch {
  643.                 currentState = State.Error;
  644.                 throw;
  645.             }
  646.         }
  647.        
  648.         // Writes out a processing instruction with a space between the name and text as follows: <?name text?>
  649.         public override void WriteProcessingInstruction(string name, string text)
  650.         {
  651.             try {
  652.                 if (null != text && text.IndexOf("?>", StringComparison.Ordinal) >= 0) {
  653.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidPiChars));
  654.                 }
  655.                 if (0 == String.Compare(name, "xml", StringComparison.OrdinalIgnoreCase) && this.stateTable == stateTableDocument) {
  656.                     throw new ArgumentException(Res.GetString(Res.Xml_DupXmlDecl));
  657.                 }
  658.                 AutoComplete(Token.PI);
  659.                 InternalWriteProcessingInstruction(name, text);
  660.             }
  661.             catch {
  662.                 currentState = State.Error;
  663.                 throw;
  664.             }
  665.         }
  666.        
  667.         // Writes out an entity reference as follows: "&"+name+";".
  668.         public override void WriteEntityRef(string name)
  669.         {
  670.             try {
  671.                 ValidateName(name, false);
  672.                 AutoComplete(Token.Content);
  673.                 xmlEncoder.WriteEntityRef(name);
  674.             }
  675.             catch {
  676.                 currentState = State.Error;
  677.                 throw;
  678.             }
  679.         }
  680.        
  681.         // Forces the generation of a character entity for the specified Unicode character value.
  682.         public override void WriteCharEntity(char ch)
  683.         {
  684.             try {
  685.                 AutoComplete(Token.Content);
  686.                 xmlEncoder.WriteCharEntity(ch);
  687.             }
  688.             catch {
  689.                 currentState = State.Error;
  690.                 throw;
  691.             }
  692.         }
  693.        
  694.         // Writes out the given whitespace.
  695.         public override void WriteWhitespace(string ws)
  696.         {
  697.             try {
  698.                 if (null == ws || ws.Length == 0) {
  699.                     throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace));
  700.                 }
  701.                
  702.                 if (!xmlCharType.IsOnlyWhitespace(ws)) {
  703.                     throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace));
  704.                 }
  705.                 AutoComplete(Token.Whitespace);
  706.                 xmlEncoder.Write(ws);
  707.             }
  708.             catch {
  709.                 currentState = State.Error;
  710.                 throw;
  711.             }
  712.         }
  713.        
  714.         // Writes out the specified text content.
  715.         public override void WriteString(string text)
  716.         {
  717.             try {
  718.                 if (null != text && text.Length != 0) {
  719.                     AutoComplete(Token.Content);
  720.                     xmlEncoder.Write(text);
  721.                 }
  722.             }
  723.             catch {
  724.                 currentState = State.Error;
  725.                 throw;
  726.             }
  727.         }
  728.        
  729.         // Writes out the specified surrogate pair as a character entity.
  730.         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  731.         {
  732.             try {
  733.                 AutoComplete(Token.Content);
  734.                 xmlEncoder.WriteSurrogateCharEntity(lowChar, highChar);
  735.             }
  736.             catch {
  737.                 currentState = State.Error;
  738.                 throw;
  739.             }
  740.         }
  741.        
  742.        
  743.         // Writes out the specified text content.
  744.         public override void WriteChars(char[] buffer, int index, int count)
  745.         {
  746.             try {
  747.                 AutoComplete(Token.Content);
  748.                 xmlEncoder.Write(buffer, index, count);
  749.             }
  750.             catch {
  751.                 currentState = State.Error;
  752.                 throw;
  753.             }
  754.         }
  755.        
  756.         // Writes raw markup from the specified character buffer.
  757.         public override void WriteRaw(char[] buffer, int index, int count)
  758.         {
  759.             try {
  760.                 AutoComplete(Token.RawData);
  761.                 xmlEncoder.WriteRaw(buffer, index, count);
  762.             }
  763.             catch {
  764.                 currentState = State.Error;
  765.                 throw;
  766.             }
  767.         }
  768.        
  769.         // Writes raw markup from the specified character string.
  770.         public override void WriteRaw(string data)
  771.         {
  772.             try {
  773.                 AutoComplete(Token.RawData);
  774.                 xmlEncoder.WriteRawWithSurrogateChecking(data);
  775.             }
  776.             catch {
  777.                 currentState = State.Error;
  778.                 throw;
  779.             }
  780.         }
  781.        
  782.         // Encodes the specified binary bytes as base64 and writes out the resulting text.
  783.         public override void WriteBase64(byte[] buffer, int index, int count)
  784.         {
  785.             try {
  786.                 if (!this.flush) {
  787.                     AutoComplete(Token.Base64);
  788.                 }
  789.                
  790.                 this.flush = true;
  791.                 // No need for us to explicitly validate the args. The StreamWriter will do
  792.                 // it for us.
  793.                 if (null == this.base64Encoder) {
  794.                     this.base64Encoder = new XmlTextWriterBase64Encoder(xmlEncoder);
  795.                 }
  796.                 // Encode will call WriteRaw to write out the encoded characters
  797.                 this.base64Encoder.Encode(buffer, index, count);
  798.             }
  799.             catch {
  800.                 currentState = State.Error;
  801.                 throw;
  802.             }
  803.         }
  804.        
  805.        
  806.         // Encodes the specified binary bytes as binhex and writes out the resulting text.
  807.         public override void WriteBinHex(byte[] buffer, int index, int count)
  808.         {
  809.             try {
  810.                 AutoComplete(Token.Content);
  811.                 BinHexEncoder.Encode(buffer, index, count, this);
  812.             }
  813.             catch {
  814.                 currentState = State.Error;
  815.                 throw;
  816.             }
  817.         }
  818.        
  819.         // Returns the state of the XmlWriter.
  820.         public override WriteState WriteState {
  821.             get {
  822.                 switch (this.currentState) {
  823.                     case State.Start:
  824.                         return WriteState.Start;
  825.                     case State.Prolog:
  826.                     case State.PostDTD:
  827.                         return WriteState.Prolog;
  828.                     case State.Element:
  829.                         return WriteState.Element;
  830.                     case State.Attribute:
  831.                     case State.AttrOnly:
  832.                         return WriteState.Attribute;
  833.                     case State.Content:
  834.                     case State.Epilog:
  835.                         return WriteState.Content;
  836.                     case State.Error:
  837.                         return WriteState.Error;
  838.                     case State.Closed:
  839.                         return WriteState.Closed;
  840.                     default:
  841.                         Debug.Assert(false);
  842.                         return WriteState.Error;
  843.                 }
  844.             }
  845.         }
  846.        
  847.         // Closes the XmlWriter and the underlying stream/TextWriter.
  848.         public override void Close()
  849.         {
  850.             try {
  851.                 AutoCompleteAll();
  852.             }
  853.             catch {
  854.                 // never fail
  855.             }
  856.             finally {
  857.                 this.currentState = State.Closed;
  858.                 textWriter.Close();
  859.             }
  860.         }
  861.        
  862.         // Flushes whatever is in the buffer to the underlying stream/TextWriter and flushes the underlying stream/TextWriter.
  863.         public override void Flush()
  864.         {
  865.             textWriter.Flush();
  866.         }
  867.        
  868.         // Writes out the specified name, ensuring it is a valid Name according to the XML specification
  869.         // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name
  870.         public override void WriteName(string name)
  871.         {
  872.             try {
  873.                 AutoComplete(Token.Content);
  874.                 InternalWriteName(name, false);
  875.             }
  876.             catch {
  877.                 currentState = State.Error;
  878.                 throw;
  879.             }
  880.         }
  881.        
  882.         // Writes out the specified namespace-qualified name by looking up the prefix that is in scope for the given namespace.
  883.         public override void WriteQualifiedName(string localName, string ns)
  884.         {
  885.             try {
  886.                 AutoComplete(Token.Content);
  887.                 if (this.namespaces) {
  888.                     if (ns != null && ns.Length != 0 && ns != stack[top].defaultNs) {
  889.                         string prefix = FindPrefix(ns);
  890.                         if (prefix == null) {
  891.                             if (this.currentState != State.Attribute) {
  892.                                 throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns));
  893.                             }
  894.                             prefix = GeneratePrefix();
  895.                             // need a prefix if
  896.                             PushNamespace(prefix, ns, false);
  897.                         }
  898.                         if (prefix.Length != 0) {
  899.                             InternalWriteName(prefix, true);
  900.                             textWriter.Write(':');
  901.                         }
  902.                     }
  903.                 }
  904.                 else if (ns != null && ns.Length != 0) {
  905.                     throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces));
  906.                 }
  907.                 InternalWriteName(localName, true);
  908.             }
  909.             catch {
  910.                 currentState = State.Error;
  911.                 throw;
  912.             }
  913.         }
  914.        
  915.         // Returns the closest prefix defined in the current namespace scope for the specified namespace URI.
  916.         public override string LookupPrefix(string ns)
  917.         {
  918.             if (ns == null || ns.Length == 0) {
  919.                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  920.             }
  921.             string s = FindPrefix(ns);
  922.             if (s == null && ns == stack[top].defaultNs) {
  923.                 s = string.Empty;
  924.             }
  925.             return s;
  926.         }
  927.        
  928.         // Gets an XmlSpace representing the current xml:space scope.
  929.         public override XmlSpace XmlSpace {
  930.             get {
  931.                 for (int i = top; i > 0; i--) {
  932.                     XmlSpace xs = stack[i].xmlSpace;
  933.                     if (xs != XmlSpace.None)
  934.                         return xs;
  935.                 }
  936.                 return XmlSpace.None;
  937.             }
  938.         }
  939.        
  940.         // Gets the current xml:lang scope.
  941.         public override string XmlLang {
  942.             get {
  943.                 for (int i = top; i > 0; i--) {
  944.                     string xlang = stack[i].xmlLang;
  945.                     if (xlang != null)
  946.                         return xlang;
  947.                 }
  948.                 return null;
  949.             }
  950.         }
  951.        
  952.         // Writes out the specified name, ensuring it is a valid NmToken
  953.         // according to the XML specification (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name).
  954.         public override void WriteNmToken(string name)
  955.         {
  956.             try {
  957.                 AutoComplete(Token.Content);
  958.                
  959.                 if (name == null || name.Length == 0) {
  960.                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  961.                 }
  962.                 if (!xmlCharType.IsNmToken(name)) {
  963.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name));
  964.                 }
  965.                 textWriter.Write(name);
  966.             }
  967.             catch {
  968.                 currentState = State.Error;
  969.                 throw;
  970.             }
  971.         }
  972.        
  973.         //
  974.         // Private implementation methods
  975.         //
  976.         void StartDocument(int standalone)
  977.         {
  978.             try {
  979.                 if (this.currentState != State.Start) {
  980.                     throw new InvalidOperationException(Res.GetString(Res.Xml_NotTheFirst));
  981.                 }
  982.                 this.stateTable = stateTableDocument;
  983.                 this.currentState = State.Prolog;
  984.                
  985.                 StringBuilder bufBld = new StringBuilder(128);
  986.                 bufBld.Append("version=" + quoteChar + "1.0" + quoteChar);
  987.                 if (this.encoding != null) {
  988.                     bufBld.Append(" encoding=");
  989.                     bufBld.Append(quoteChar);
  990.                     bufBld.Append(this.encoding.WebName);
  991.                     bufBld.Append(quoteChar);
  992.                 }
  993.                 if (standalone >= 0) {
  994.                     bufBld.Append(" standalone=");
  995.                     bufBld.Append(quoteChar);
  996.                     bufBld.Append(standalone == 0 ? "no" : "yes");
  997.                     bufBld.Append(quoteChar);
  998.                 }
  999.                 InternalWriteProcessingInstruction("xml", bufBld.ToString());
  1000.             }
  1001.             catch {
  1002.                 currentState = State.Error;
  1003.                 throw;
  1004.             }
  1005.         }
  1006.        
  1007.         void AutoComplete(Token token)
  1008.         {
  1009.             if (this.currentState == State.Closed) {
  1010.                 throw new InvalidOperationException(Res.GetString(Res.Xml_Closed));
  1011.             }
  1012.             else if (this.currentState == State.Error) {
  1013.                 throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], stateName[(int)State.Error]));
  1014.             }
  1015.            
  1016.             State newState = this.stateTable[(int)token * 8 + (int)this.currentState];
  1017.             if (newState == State.Error) {
  1018.                 throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], stateName[(int)this.currentState]));
  1019.             }
  1020.            
  1021.             switch (token) {
  1022.                 case Token.Doctype:
  1023.                     if (this.indented && this.currentState != State.Start) {
  1024.                         Indent(false);
  1025.                     }
  1026.                     break;
  1027.                 case Token.StartElement:
  1028.                 case Token.Comment:
  1029.                 case Token.PI:
  1030.                 case Token.CData:
  1031.                    
  1032.                     if (this.currentState == State.Attribute) {
  1033.                         WriteEndAttributeQuote();
  1034.                         WriteEndStartTag(false);
  1035.                     }
  1036.                     else if (this.currentState == State.Element) {
  1037.                         WriteEndStartTag(false);
  1038.                     }
  1039.                     if (token == Token.CData) {
  1040.                         stack[top].mixed = true;
  1041.                     }
  1042.                     else if (this.indented && this.currentState != State.Start) {
  1043.                         Indent(false);
  1044.                     }
  1045.                     break;
  1046.                 case Token.EndElement:
  1047.                 case Token.LongEndElement:
  1048.                    
  1049.                     if (this.flush) {
  1050.                         FlushEncoders();
  1051.                     }
  1052.                     if (this.currentState == State.Attribute) {
  1053.                         WriteEndAttributeQuote();
  1054.                     }
  1055.                     if (this.currentState == State.Content) {
  1056.                         token = Token.LongEndElement;
  1057.                     }
  1058.                     else {
  1059.                         WriteEndStartTag(token == Token.EndElement);
  1060.                     }
  1061.                     if (stateTableDocument == this.stateTable && top == 1) {
  1062.                         newState = State.Epilog;
  1063.                     }
  1064.                     break;
  1065.                 case Token.StartAttribute:
  1066.                    
  1067.                     if (this.flush) {
  1068.                         FlushEncoders();
  1069.                     }
  1070.                     if (this.currentState == State.Attribute) {
  1071.                         WriteEndAttributeQuote();
  1072.                         textWriter.Write(' ');
  1073.                     }
  1074.                     else if (this.currentState == State.Element) {
  1075.                         textWriter.Write(' ');
  1076.                     }
  1077.                     break;
  1078.                 case Token.EndAttribute:
  1079.                    
  1080.                     if (this.flush) {
  1081.                         FlushEncoders();
  1082.                     }
  1083.                     WriteEndAttributeQuote();
  1084.                     break;
  1085.                 case Token.Whitespace:
  1086.                 case Token.Content:
  1087.                 case Token.RawData:
  1088.                 case Token.Base64:
  1089.                    
  1090.                    
  1091.                     if (token != Token.Base64 && this.flush) {
  1092.                         FlushEncoders();
  1093.                     }
  1094.                     if (this.currentState == State.Element && this.lastToken != Token.Content) {
  1095.                         WriteEndStartTag(false);
  1096.                     }
  1097.                     if (newState == State.Content) {
  1098.                         stack[top].mixed = true;
  1099.                     }
  1100.                     break;
  1101.                 default:
  1102.                    
  1103.                     throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
  1104.                     break;
  1105.             }
  1106.             this.currentState = newState;
  1107.             this.lastToken = token;
  1108.         }
  1109.        
  1110.         void AutoCompleteAll()
  1111.         {
  1112.             if (this.flush) {
  1113.                 FlushEncoders();
  1114.             }
  1115.             while (top > 0) {
  1116.                 WriteEndElement();
  1117.             }
  1118.         }
  1119.        
  1120.         void InternalWriteEndElement(bool longFormat)
  1121.         {
  1122.             try {
  1123.                 if (top <= 0) {
  1124.                     throw new InvalidOperationException(Res.GetString(Res.Xml_NoStartTag));
  1125.                 }
  1126.                 // if we are in the element, we need to close it.
  1127.                 AutoComplete(longFormat ? Token.LongEndElement : Token.EndElement);
  1128.                 if (this.lastToken == Token.LongEndElement) {
  1129.                     if (this.indented) {
  1130.                         Indent(true);
  1131.                     }
  1132.                     textWriter.Write('<');
  1133.                     textWriter.Write('/');
  1134.                     if (this.namespaces && stack[top].prefix != null) {
  1135.                         textWriter.Write(stack[top].prefix);
  1136.                         textWriter.Write(':');
  1137.                     }
  1138.                     textWriter.Write(stack[top].name);
  1139.                     textWriter.Write('>');
  1140.                 }
  1141.                
  1142.                 // pop namespaces
  1143.                 int prevNsTop = stack[top].prevNsTop;
  1144.                 if (useNsHashtable && prevNsTop < nsTop) {
  1145.                     PopNamespaces(prevNsTop + 1, nsTop);
  1146.                 }
  1147.                 nsTop = prevNsTop;
  1148.                 top--;
  1149.             }
  1150.             catch {
  1151.                 currentState = State.Error;
  1152.                 throw;
  1153.             }
  1154.         }
  1155.        
  1156.         void WriteEndStartTag(bool empty)
  1157.         {
  1158.             xmlEncoder.StartAttribute(false);
  1159.             for (int i = nsTop; i > stack[top].prevNsTop; i--) {
  1160.                 if (!nsStack[i].declared) {
  1161.                     textWriter.Write(" xmlns");
  1162.                     textWriter.Write(':');
  1163.                     textWriter.Write(nsStack[i].prefix);
  1164.                     textWriter.Write('=');
  1165.                     textWriter.Write(this.quoteChar);
  1166.                     xmlEncoder.Write(nsStack[i].ns);
  1167.                     textWriter.Write(this.quoteChar);
  1168.                 }
  1169.             }
  1170.             // Default
  1171.             if ((stack[top].defaultNs != stack[top - 1].defaultNs) && (stack[top].defaultNsState == NamespaceState.DeclaredButNotWrittenOut)) {
  1172.                 textWriter.Write(" xmlns");
  1173.                 textWriter.Write('=');
  1174.                 textWriter.Write(this.quoteChar);
  1175.                 xmlEncoder.Write(stack[top].defaultNs);
  1176.                 textWriter.Write(this.quoteChar);
  1177.                 stack[top].defaultNsState = NamespaceState.DeclaredAndWrittenOut;
  1178.             }
  1179.             xmlEncoder.EndAttribute();
  1180.             if (empty) {
  1181.                 textWriter.Write(" /");
  1182.             }
  1183.             textWriter.Write('>');
  1184.         }
  1185.        
  1186.         void WriteEndAttributeQuote()
  1187.         {
  1188.             if (this.specialAttr != SpecialAttr.None) {
  1189.                 // Ok, now to handle xmlspace, etc.
  1190.                 HandleSpecialAttribute();
  1191.             }
  1192.             xmlEncoder.EndAttribute();
  1193.             textWriter.Write(this.curQuoteChar);
  1194.         }
  1195.        
  1196.         void Indent(bool beforeEndElement)
  1197.         {
  1198.             // pretty printing.
  1199.             if (top == 0) {
  1200.                 textWriter.WriteLine();
  1201.             }
  1202.             else if (!stack[top].mixed) {
  1203.                 textWriter.WriteLine();
  1204.                 int i = beforeEndElement ? top - 1 : top;
  1205.                 for (i *= this.indentation; i > 0; i--) {
  1206.                     textWriter.Write(this.indentChar);
  1207.                 }
  1208.             }
  1209.         }
  1210.        
  1211.         // pushes new namespace scope, and returns generated prefix, if one
  1212.         // was needed to resolve conflicts.
  1213.         void PushNamespace(string prefix, string ns, bool declared)
  1214.         {
  1215.             if (XmlReservedNs.NsXmlNs == ns) {
  1216.                 throw new ArgumentException(Res.GetString(Res.Xml_CanNotBindToReservedNamespace));
  1217.             }
  1218.            
  1219.             if (prefix == null) {
  1220.                 switch (stack[top].defaultNsState) {
  1221.                     case NamespaceState.DeclaredButNotWrittenOut:
  1222.                         Debug.Assert(declared == true, "Unexpected situation!!");
  1223.                         // the first namespace that the user gave us is what we
  1224.                         // like to keep.
  1225.                         break;
  1226.                     case NamespaceState.Uninitialized:
  1227.                     case NamespaceState.NotDeclaredButInScope:
  1228.                         // we now got a brand new namespace that we need to remember
  1229.                         stack[top].defaultNs = ns;
  1230.                         break;
  1231.                     default:
  1232.                         Debug.Assert(false, "Should have never come here");
  1233.                         return;
  1234.                 }
  1235.                 stack[top].defaultNsState = (declared ? NamespaceState.DeclaredAndWrittenOut : NamespaceState.DeclaredButNotWrittenOut);
  1236.             }
  1237.             else {
  1238.                 if (prefix.Length != 0 && ns.Length == 0) {
  1239.                     throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
  1240.                 }
  1241.                
  1242.                 int existingNsIndex = LookupNamespace(prefix);
  1243.                 if (existingNsIndex != -1 && nsStack[existingNsIndex].ns == ns) {
  1244.                     // it is already in scope.
  1245.                     if (declared) {
  1246.                         nsStack[existingNsIndex].declared = true;
  1247.                     }
  1248.                 }
  1249.                 else {
  1250.                     // see if prefix conflicts for the current element
  1251.                     if (declared) {
  1252.                         if (existingNsIndex != -1 && existingNsIndex > stack[top].prevNsTop) {
  1253.                             nsStack[existingNsIndex].declared = true;
  1254.                             // old one is silenced now
  1255.                         }
  1256.                     }
  1257.                     AddNamespace(prefix, ns, declared);
  1258.                 }
  1259.             }
  1260.         }
  1261.        
  1262.         void AddNamespace(string prefix, string ns, bool declared)
  1263.         {
  1264.             int nsIndex = ++nsTop;
  1265.             if (nsIndex == nsStack.Length) {
  1266.                 Namespace[] newStack = new Namespace[nsIndex * 2];
  1267.                 Array.Copy(nsStack, newStack, nsIndex);
  1268.                 nsStack = newStack;
  1269.             }
  1270.             nsStack[nsIndex].Set(prefix, ns, declared);
  1271.            
  1272.             if (useNsHashtable) {
  1273.                 AddToNamespaceHashtable(nsIndex);
  1274.             }
  1275.             else if (nsIndex == MaxNamespacesWalkCount) {
  1276.                 // add all
  1277.                 nsHashtable = new Dictionary<string, int>(new SecureStringHasher());
  1278.                 for (int i = 0; i <= nsIndex; i++) {
  1279.                     AddToNamespaceHashtable(i);
  1280.                 }
  1281.                 useNsHashtable = true;
  1282.             }
  1283.         }
  1284.        
  1285.         void AddToNamespaceHashtable(int namespaceIndex)
  1286.         {
  1287.             string prefix = nsStack[namespaceIndex].prefix;
  1288.             int existingNsIndex;
  1289.             if (nsHashtable.TryGetValue(prefix, out existingNsIndex)) {
  1290.                 nsStack[namespaceIndex].prevNsIndex = existingNsIndex;
  1291.             }
  1292.             nsHashtable[prefix] = namespaceIndex;
  1293.         }
  1294.        
  1295.         private void PopNamespaces(int indexFrom, int indexTo)
  1296.         {
  1297.             Debug.Assert(useNsHashtable);
  1298.             for (int i = indexTo; i >= indexFrom; i--) {
  1299.                 Debug.Assert(nsHashtable.ContainsKey(nsStack[i].prefix));
  1300.                 if (nsStack[i].prevNsIndex == -1) {
  1301.                     nsHashtable.Remove(nsStack[i].prefix);
  1302.                 }
  1303.                 else {
  1304.                     nsHashtable[nsStack[i].prefix] = nsStack[i].prevNsIndex;
  1305.                 }
  1306.             }
  1307.         }
  1308.        
  1309.         string GeneratePrefix()
  1310.         {
  1311.             int temp = stack[top].prefixCount++ + 1;
  1312.             return "d" + top.ToString("d", CultureInfo.InvariantCulture) + "p" + temp.ToString("d", CultureInfo.InvariantCulture);
  1313.         }
  1314.        
  1315.         void InternalWriteProcessingInstruction(string name, string text)
  1316.         {
  1317.             textWriter.Write("<?");
  1318.             ValidateName(name, false);
  1319.             textWriter.Write(name);
  1320.             textWriter.Write(' ');
  1321.             if (null != text) {
  1322.                 xmlEncoder.WriteRawWithSurrogateChecking(text);
  1323.             }
  1324.             textWriter.Write("?>");
  1325.         }
  1326.        
  1327.         int LookupNamespace(string prefix)
  1328.         {
  1329.             if (useNsHashtable) {
  1330.                 int nsIndex;
  1331.                 if (nsHashtable.TryGetValue(prefix, out nsIndex)) {
  1332.                     return nsIndex;
  1333.                 }
  1334.             }
  1335.             else {
  1336.                 for (int i = nsTop; i >= 0; i--) {
  1337.                     if (nsStack[i].prefix == prefix) {
  1338.                         return i;
  1339.                     }
  1340.                 }
  1341.             }
  1342.             return -1;
  1343.         }
  1344.        
  1345.         int LookupNamespaceInCurrentScope(string prefix)
  1346.         {
  1347.             if (useNsHashtable) {
  1348.                 int nsIndex;
  1349.                 if (nsHashtable.TryGetValue(prefix, out nsIndex)) {
  1350.                     if (nsIndex > stack[top].prevNsTop) {
  1351.                         return nsIndex;
  1352.                     }
  1353.                 }
  1354.             }
  1355.             else {
  1356.                 for (int i = nsTop; i > stack[top].prevNsTop; i--) {
  1357.                     if (nsStack[i].prefix == prefix) {
  1358.                         return i;
  1359.                     }
  1360.                 }
  1361.             }
  1362.             return -1;
  1363.         }
  1364.        
  1365.         string FindPrefix(string ns)
  1366.         {
  1367.             for (int i = nsTop; i >= 0; i--) {
  1368.                 if (nsStack[i].ns == ns) {
  1369.                     if (LookupNamespace(nsStack[i].prefix) == i) {
  1370.                         return nsStack[i].prefix;
  1371.                     }
  1372.                 }
  1373.             }
  1374.             return null;
  1375.         }
  1376.        
  1377.         // There are three kind of strings we write out - Name, LocalName and Prefix.
  1378.         // Both LocalName and Prefix can be represented with NCName == false and Name
  1379.         // can be represented as NCName == true
  1380.        
  1381.         void InternalWriteName(string name, bool NCName)
  1382.         {
  1383.             ValidateName(name, NCName);
  1384.             textWriter.Write(name);
  1385.         }
  1386.        
  1387.         unsafe private void ValidateName(string name, bool NCName)
  1388.         {
  1389.             if (name == null || name.Length == 0) {
  1390.                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  1391.             }
  1392.             int nameLength = name.Length;
  1393.             int position = 0;
  1394.             int colonPosition = -1;
  1395.            
  1396.             if (namespaces) {
  1397.                 if ((xmlCharType.charProperties[name[position]] & XmlCharType.fNCStartName) != 0) {
  1398.                     Continue:
  1399.                     // if (xmlCharType.IsStartNCNameChar(name[position])) {
  1400.                     position++;
  1401.                     while (position < nameLength && (xmlCharType.charProperties[name[position]] & XmlCharType.fNCName) != 0) {
  1402.                         // xmlCharType.IsNCNameChar(name[position])) {
  1403.                         position++;
  1404.                     }
  1405.                     if (position == nameLength) {
  1406.                         return;
  1407.                     }
  1408.                     if (name[position] == ':') {
  1409.                         if (!NCName) {
  1410.                             if (colonPosition == -1) {
  1411.                                 if (position + 1 < nameLength) {
  1412.                                     colonPosition = position;
  1413.                                     // We should check if the character after ':' is a valid start name
  1414.                                     // character but we can't do that because of backward compatilibity
  1415.                                     goto Continue;
  1416.                                 }
  1417.                             }
  1418.                         }
  1419.                     }
  1420.                 }
  1421.             }
  1422.             else {
  1423.                 if ((xmlCharType.charProperties[name[0]] & XmlCharType.fNCStartName) != 0 || name[0] == ':') {
  1424.                     // if (xmlCharType.IsStartNameChar(name[0])) {
  1425.                     position++;
  1426.                     while (position < nameLength && ((xmlCharType.charProperties[name[position]] & XmlCharType.fNCName) != 0 || name[position] == ':')) {
  1427.                         // xmlCharType.IsNameChar(name[position])) {
  1428.                         position++;
  1429.                     }
  1430.                     if (position == nameLength) {
  1431.                         return;
  1432.                     }
  1433.                 }
  1434.             }
  1435.             throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name));
  1436.         }
  1437.        
  1438.         void HandleSpecialAttribute()
  1439.         {
  1440.             string value = xmlEncoder.AttributeValue;
  1441.             switch (this.specialAttr) {
  1442.                 case SpecialAttr.XmlLang:
  1443.                     stack[top].xmlLang = value;
  1444.                     break;
  1445.                 case SpecialAttr.XmlSpace:
  1446.                     // validate XmlSpace attribute
  1447.                     value = XmlConvert.TrimString(value);
  1448.                     if (value == "default") {
  1449.                         stack[top].xmlSpace = XmlSpace.Default;
  1450.                     }
  1451.                     else if (value == "preserve") {
  1452.                         stack[top].xmlSpace = XmlSpace.Preserve;
  1453.                     }
  1454.                     else {
  1455.                         throw new ArgumentException(Res.GetString(Res.Xml_InvalidXmlSpace, value));
  1456.                     }
  1457.                     break;
  1458.                 case SpecialAttr.XmlNs:
  1459.                     VerifyPrefixXml(this.prefixForXmlNs, value);
  1460.                     PushNamespace(this.prefixForXmlNs, value, true);
  1461.                     break;
  1462.             }
  1463.         }
  1464.        
  1465.        
  1466.         void VerifyPrefixXml(string prefix, string ns)
  1467.         {
  1468.            
  1469.             if (prefix != null && prefix.Length == 3) {
  1470.                 if ((prefix[0] == 'x' || prefix[0] == 'X') && (prefix[1] == 'm' || prefix[1] == 'M') && (prefix[2] == 'l' || prefix[2] == 'L')) {
  1471.                     if (XmlReservedNs.NsXml != ns) {
  1472.                         throw new ArgumentException(Res.GetString(Res.Xml_InvalidPrefix));
  1473.                     }
  1474.                 }
  1475.             }
  1476.         }
  1477.        
  1478.         void PushStack()
  1479.         {
  1480.             if (top == stack.Length - 1) {
  1481.                 TagInfo[] na = new TagInfo[stack.Length + 10];
  1482.                 if (top > 0)
  1483.                     Array.Copy(stack, na, top + 1);
  1484.                 stack = na;
  1485.             }
  1486.            
  1487.             top++;
  1488.             // Move up stack
  1489.             stack[top].Init(nsTop);
  1490.         }
  1491.        
  1492.         void FlushEncoders()
  1493.         {
  1494.             if (null != this.base64Encoder) {
  1495.                 // The Flush will call WriteRaw to write out the rest of the encoded characters
  1496.                 this.base64Encoder.Flush();
  1497.             }
  1498.             this.flush = false;
  1499.         }
  1500.     }
  1501. }

Developer Fusion