The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Runtime \ XmlState

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlQueryOutput.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.Xml;
  17. using System.Xml.XPath;
  18. using System.Xml.Schema;
  19. using System.Collections;
  20. using System.Diagnostics;
  21. using System.Globalization;
  22. using System.Xml.Xsl.Qil;
  23. using System.ComponentModel;
  24. namespace System.Xml.Xsl.Runtime
  25. {
  26.     using Res = System.Xml.Utils.Res;
  27.    
  28.     internal enum XmlState
  29.     {
  30.         WithinSequence = 0,
  31.         // Adding items to a top-level sequence
  32.         EnumAttrs,
  33.         // Adding attributes to an element
  34.         WithinContent,
  35.         // Adding content to an element
  36.         WithinAttr,
  37.         // Adding text to an attribute
  38.         WithinNmsp,
  39.         // Adding text to an namespace
  40.         WithinComment,
  41.         // Adding text to a comment
  42.         WithinPI
  43.         // Adding text to a processing instruction
  44.     }
  45.    
  46.    
  47.     /// <summary>
  48.     /// At run-time, a number of checks may need to be made in order to generate the correct sequence of calls
  49.     /// to XmlRawWriter:
  50.     /// 1. Well-formedness: Illegal state transitions, StartContent detection, no-content element detection
  51.     /// 2. Cached attributes: In XSLT, attributes override previously constructed attributes with the same name,
  52.     /// meaning that attribute names and values cannot be prematurely sent to XmlRawWriter.
  53.     /// 3. Cached namespaces: All namespaces are tracked in order to ensure adequate namespaces and to ensure
  54.     /// minimal (or something close) namespaces.
  55.     /// </summary>
  56.     [EditorBrowsable(EditorBrowsableState.Never)]
  57.     public sealed class XmlQueryOutput : XmlWriter
  58.     {
  59.         // Never set these fields directly--instead use corresponding properties
  60.         private XmlRawWriter xwrt;
  61.         // Output to XmlRawWriter--get and set this using the Writer property
  62.         // It is OK to set these properties directly
  63.         private XmlQueryRuntime runtime;
  64.         // The XmlQueryRuntime instance that keeps global state
  65.         private XmlAttributeCache attrCache;
  66.         // Cache used to detect duplicate attributes
  67.         private int depth;
  68.         // Depth of the currently constructing tree
  69.         private XmlState xstate;
  70.         // Current XML state
  71.         private XmlSequenceWriter seqwrt;
  72.         // Current XmlSequenceWriter
  73.         private XmlNamespaceManager nsmgr;
  74.         // Output namespace manager
  75.         private int cntNmsp;
  76.         // Number of pending namespaces
  77.         private Hashtable conflictPrefixes;
  78.         // Remembers prefixes that were auto-generated previously in case they can be reused
  79.         private int prefixIndex;
  80.         // Counter used to auto-generate non-conflicting attribute prefixes
  81.         private string piTarget, piText;
  82.         // Cache pi target and text
  83.         private string commentText;
  84.         // Cache comment text
  85.         private string nmspPrefix, nmspText;
  86.         // Cache namespace prefix and text
  87.         private Stack stkNames;
  88.         // Keep stack of name parts computed during StartElement
  89.         private XPathNodeType rootType;
  90.         // NodeType of the root of the tree
  91.         private bool useDefNmsp;
  92.         // True if xmlns="" is either locally declared or used by element name
  93.         /// <summary>
  94.         /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
  95.         /// Initialize output state to accept top-level sequences.
  96.         /// </summary>
  97.         internal XmlQueryOutput(XmlQueryRuntime runtime, XmlSequenceWriter seqwrt)
  98.         {
  99.             this.runtime = runtime;
  100.             this.seqwrt = seqwrt;
  101.             this.xstate = XmlState.WithinSequence;
  102.         }
  103.        
  104.         /// <summary>
  105.         /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
  106.         /// Initialize output state to accept Rtf content (top-level sequences are therefore prohibited).
  107.         /// </summary>
  108.         internal XmlQueryOutput(XmlQueryRuntime runtime, XmlEventCache xwrt)
  109.         {
  110.             this.runtime = runtime;
  111.             this.xwrt = xwrt;
  112.             this.xstate = XmlState.WithinContent;
  113.             this.depth = 1;
  114.             this.rootType = XPathNodeType.Root;
  115.         }
  116.        
  117.         /// <summary>
  118.         /// Sequence writer to which output is directed by this class.
  119.         /// </summary>
  120.         internal XmlSequenceWriter SequenceWriter {
  121.             get { return this.seqwrt; }
  122.         }
  123.        
  124.         /// <summary>
  125.         /// Raw writer to which output is directed by this class.
  126.         /// </summary>
  127.         internal XmlRawWriter Writer {
  128.             get { return this.xwrt; }
  129.             set {
  130.                 // If new writer might remove itself from pipeline, have it callback on this method when it's ready to go
  131.                 IRemovableWriter removable = value as IRemovableWriter;
  132.                 if (removable != null)
  133.                     removable.OnRemoveWriterEvent = SetWrappedWriter;
  134.                
  135.                 this.xwrt = value;
  136.             }
  137.         }
  138.        
  139.         /// <summary>
  140.         /// This method will be called if "xwrt" is a writer which no longer needs to be part of the pipeline and
  141.         /// wishes to replace itself with a different writer. For example, the auto-detect writer replaces itself
  142.         /// with the Html or Xml writer once it has determined which output mode to use.
  143.         /// </summary>
  144.         private void SetWrappedWriter(XmlRawWriter writer)
  145.         {
  146.             // Reuse XmlAttributeCache so that it doesn't have to be recreated every time
  147.             if (Writer is XmlAttributeCache)
  148.                 this.attrCache = (XmlAttributeCache)Writer;
  149.            
  150.             Writer = writer;
  151.         }
  152.        
  153.        
  154.         //-----------------------------------------------
  155.         // XmlWriter methods
  156.         //-----------------------------------------------
  157.        
  158.         /// <summary>
  159.         /// Should never be called.
  160.         /// </summary>
  161.         public override void WriteStartDocument()
  162.         {
  163.             throw new NotSupportedException();
  164.         }
  165.        
  166.         /// <summary>
  167.         /// Should never be called.
  168.         /// </summary>
  169.         public override void WriteStartDocument(bool standalone)
  170.         {
  171.             throw new NotSupportedException();
  172.         }
  173.        
  174.         /// <summary>
  175.         /// Should never be called.
  176.         /// </summary>
  177.         public override void WriteEndDocument()
  178.         {
  179.             throw new NotSupportedException();
  180.         }
  181.        
  182.         /// <summary>
  183.         /// Should never be called.
  184.         /// </summary>
  185.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  186.         {
  187.             throw new NotSupportedException();
  188.         }
  189.        
  190.         /// <summary>
  191.         /// Before calling XmlRawWriter.WriteStartElement(), perform various checks to ensure well-formedness.
  192.         /// </summary>
  193.         public override void WriteStartElement(string prefix, string localName, string ns)
  194.         {
  195.             Debug.Assert(prefix != null && localName != null && localName.Length != 0 && ns != null, "Invalid argument");
  196.             Debug.Assert(ValidateNames.ValidateName(prefix, localName, ns, XPathNodeType.Element, ValidateNames.Flags.All), "Name validation failed");
  197.            
  198.             // Xml state transitions
  199.             ConstructWithinContent(XPathNodeType.Element);
  200.            
  201.             // Call XmlRawWriter.WriteStartElement
  202.             WriteStartElementUnchecked(prefix, localName, ns);
  203.            
  204.             // Ensure that element's namespace declaration is declared
  205.             WriteNamespaceDeclarationUnchecked(prefix, ns);
  206.            
  207.             // Cache attributes in order to detect duplicates
  208.             if (this.attrCache == null)
  209.                 this.attrCache = new XmlAttributeCache();
  210.            
  211.             this.attrCache.Init(Writer);
  212.             Writer = this.attrCache;
  213.             this.attrCache = null;
  214.            
  215.             // Push element names onto a stack
  216.             PushElementNames(prefix, localName, ns);
  217.         }
  218.        
  219.         /// <summary>
  220.         /// Before calling XmlRawWriter.WriteEndElement(), perform various checks to ensure well-formedness.
  221.         /// </summary>
  222.         public override void WriteEndElement()
  223.         {
  224.             string prefix;
  225.             string localName;
  226.             string ns;
  227.            
  228.             // Determine whether element had no content
  229.             if (this.xstate == XmlState.EnumAttrs) {
  230.                 // No content, so call StartElementContent now
  231.                 StartElementContentUnchecked();
  232.             }
  233.            
  234.             // Call XmlRawWriter.WriteEndElement
  235.             PopElementNames(out prefix, out localName, out ns);
  236.             WriteEndElementUnchecked(prefix, localName, ns);
  237.            
  238.             // Xml state transitions
  239.             if (this.depth == 0)
  240.                 EndTree();
  241.         }
  242.        
  243.         /// <summary>
  244.         /// Same as calling WriteEndElement().
  245.         /// </summary>
  246.         public override void WriteFullEndElement()
  247.         {
  248.             WriteEndElement();
  249.         }
  250.        
  251.         /// <summary>
  252.         /// Before calling XmlRawWriter.WriteStartAttribute(), perform various checks to ensure well-formedness.
  253.         /// </summary>
  254.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  255.         {
  256.             Debug.Assert(prefix != null && localName != null && ns != null, "Invalid argument");
  257.            
  258.             if (prefix.Length == 5 && prefix == "xmlns") {
  259.                 // Handle namespace attributes that are not sent directly to WriteNamespaceDeclaration
  260.                 WriteStartNamespace(localName);
  261.             }
  262.             else {
  263.                 // All other attributes
  264.                 Debug.Assert(ValidateNames.ValidateName(prefix, localName, ns, XPathNodeType.Attribute, ValidateNames.Flags.All));
  265.                
  266.                 // Xml state transitions
  267.                 ConstructInEnumAttrs(XPathNodeType.Attribute);
  268.                
  269.                 // Check for prefix conflicts and possibly declare prefix
  270.                 if (ns.Length != 0 && this.depth != 0)
  271.                     prefix = CheckAttributePrefix(prefix, ns);
  272.                
  273.                 // Output the attribute
  274.                 WriteStartAttributeUnchecked(prefix, localName, ns);
  275.             }
  276.         }
  277.        
  278.         /// <summary>
  279.         /// Before calling XmlRawWriter.WriteEndAttribute(), perform various checks to ensure well-formedness.
  280.         /// </summary>
  281.         public override void WriteEndAttribute()
  282.         {
  283.             if (this.xstate == XmlState.WithinNmsp) {
  284.                 WriteEndNamespace();
  285.             }
  286.             else {
  287.                 WriteEndAttributeUnchecked();
  288.                
  289.                 if (this.depth == 0)
  290.                     EndTree();
  291.             }
  292.         }
  293.        
  294.         /// <summary>
  295.         /// Before writing a comment, perform various checks to ensure well-formedness.
  296.         /// </summary>
  297.         public override void WriteComment(string text)
  298.         {
  299.             WriteStartComment();
  300.             WriteCommentString(text);
  301.             WriteEndComment();
  302.         }
  303.        
  304.         /// <summary>
  305.         /// Before writing a processing instruction, perform various checks to ensure well-formedness.
  306.         /// </summary>
  307.         public override void WriteProcessingInstruction(string target, string text)
  308.         {
  309.             WriteStartProcessingInstruction(target);
  310.             WriteProcessingInstructionString(text);
  311.             WriteEndProcessingInstruction();
  312.         }
  313.        
  314.         /// <summary>
  315.         /// Should never be called.
  316.         /// </summary>
  317.         public override void WriteEntityRef(string name)
  318.         {
  319.             throw new NotSupportedException();
  320.         }
  321.        
  322.         /// <summary>
  323.         /// Should never be called.
  324.         /// </summary>
  325.         public override void WriteCharEntity(char ch)
  326.         {
  327.             throw new NotSupportedException();
  328.         }
  329.        
  330.         /// <summary>
  331.         /// Should never be called.
  332.         /// </summary>
  333.         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  334.         {
  335.             throw new NotSupportedException();
  336.         }
  337.        
  338.         /// <summary>
  339.         /// Treat whitespace as regular text.
  340.         /// </summary>
  341.         public override void WriteWhitespace(string ws)
  342.         {
  343.             throw new NotSupportedException();
  344.         }
  345.        
  346.         /// <summary>
  347.         /// Before writing text, perform various checks to ensure well-formedness.
  348.         /// </summary>
  349.         public override void WriteString(string text)
  350.         {
  351.             WriteString(text, false);
  352.         }
  353.        
  354.         /// <summary>
  355.         /// Before writing text, perform various checks to ensure well-formedness.
  356.         /// </summary>
  357.         public override void WriteChars(char[] buffer, int index, int count)
  358.         {
  359.             throw new NotSupportedException();
  360.         }
  361.        
  362.         /// <summary>
  363.         /// Write text, but do not escape special characters.
  364.         /// </summary>
  365.         public override void WriteRaw(char[] buffer, int index, int count)
  366.         {
  367.             throw new NotSupportedException();
  368.         }
  369.        
  370.         /// <summary>
  371.         /// Write text, but do not escape special characters.
  372.         /// </summary>
  373.         public override void WriteRaw(string data)
  374.         {
  375.             WriteString(data, true);
  376.         }
  377.        
  378.         /// <summary>
  379.         /// Write CData text as regular text.
  380.         /// </summary>
  381.         public override void WriteCData(string text)
  382.         {
  383.             WriteString(text, false);
  384.         }
  385.        
  386.         /// <summary>
  387.         /// Should never be called.
  388.         /// </summary>
  389.         public override void WriteBase64(byte[] buffer, int index, int count)
  390.         {
  391.             throw new NotSupportedException();
  392.         }
  393.        
  394.         /// <summary>
  395.         /// Should never be called.
  396.         /// </summary>
  397.         public override WriteState WriteState {
  398.             get {
  399.                 throw new NotSupportedException();
  400.             }
  401.         }
  402.        
  403.         /// <summary>
  404.         /// No-op.
  405.         /// </summary>
  406.         public override void Close()
  407.         {
  408.         }
  409.        
  410.         /// <summary>
  411.         /// No-op.
  412.         /// </summary>
  413.         public override void Flush()
  414.         {
  415.         }
  416.        
  417.         /// <summary>
  418.         /// Should never be called.
  419.         /// </summary>
  420.         public override string LookupPrefix(string ns)
  421.         {
  422.             throw new NotSupportedException();
  423.         }
  424.        
  425.         /// <summary>
  426.         /// Should never be called.
  427.         /// </summary>
  428.         public override XmlSpace XmlSpace {
  429.             get {
  430.                 throw new NotSupportedException();
  431.             }
  432.         }
  433.        
  434.         /// <summary>
  435.         /// Should never be called.
  436.         /// </summary>
  437.         public override string XmlLang {
  438.             get {
  439.                 throw new NotSupportedException();
  440.             }
  441.         }
  442.        
  443.        
  444.         //-----------------------------------------------
  445.         // XmlQueryOutput methods (XmlSequenceWriter)
  446.         //-----------------------------------------------
  447.        
  448.         /// <summary>
  449.         /// Call XmlSequenceWriter.StartTree() in order to start construction of a new tree.
  450.         /// </summary>
  451.         public void StartTree(XPathNodeType rootType)
  452.         {
  453.             Debug.Assert(this.xstate == XmlState.WithinSequence, "StartTree cannot be called in the " + this.xstate + " state.");
  454.             Writer = this.seqwrt.StartTree(rootType, this.nsmgr, this.runtime.NameTable);
  455.             this.rootType = rootType;
  456.             this.xstate = (rootType == XPathNodeType.Attribute || rootType == XPathNodeType.Namespace) ? XmlState.EnumAttrs : XmlState.WithinContent;
  457.         }
  458.        
  459.         /// <summary>
  460.         /// Call XmlSequenceWriter.EndTree().
  461.         /// </summary>
  462.         public void EndTree()
  463.         {
  464.             Debug.Assert(this.xstate == XmlState.EnumAttrs || this.xstate == XmlState.WithinContent, "EndTree cannot be called in the " + this.xstate + " state.");
  465.             this.seqwrt.EndTree();
  466.             this.xstate = XmlState.WithinSequence;
  467.             Writer = null;
  468.         }
  469.        
  470.        
  471.         //-----------------------------------------------
  472.         // XmlQueryOutput methods (XmlRawWriter)
  473.         //-----------------------------------------------
  474.        
  475.         /// <summary>
  476.         /// Call XmlRawWriter.WriteStartElement() with prefix, local-name, ns, and schema type.
  477.         /// </summary>
  478.         public void WriteStartElementUnchecked(string prefix, string localName, string ns)
  479.         {
  480.             Debug.Assert(this.xstate == XmlState.WithinContent, "WriteStartElement cannot be called in the " + this.xstate + " state.");
  481.             if (this.nsmgr != null)
  482.                 this.nsmgr.PushScope();
  483.             Writer.WriteStartElement(prefix, localName, ns);
  484.             this.xstate = XmlState.EnumAttrs;
  485.             this.depth++;
  486.             this.useDefNmsp = ns.Length == 0;
  487.         }
  488.        
  489.         /// <summary>
  490.         /// Call XmlRawWriter.WriteStartElement() with empty prefix, ns, and null schema type.
  491.         /// </summary>
  492.         public void WriteStartElementUnchecked(string localName)
  493.         {
  494.             WriteStartElementUnchecked(string.Empty, localName, string.Empty);
  495.         }
  496.        
  497.         /// <summary>
  498.         /// Call XmlRawWriter.StartElementContent().
  499.         /// </summary>
  500.         public void StartElementContentUnchecked()
  501.         {
  502.             Debug.Assert(this.xstate == XmlState.EnumAttrs, "StartElementContent cannot be called in the " + this.xstate + " state.");
  503.            
  504.             // Output any cached namespaces
  505.             if (this.cntNmsp != 0)
  506.                 WriteCachedNamespaces();
  507.            
  508.             Writer.StartElementContent();
  509.             this.xstate = XmlState.WithinContent;
  510.             this.useDefNmsp = false;
  511.         }
  512.        
  513.         /// <summary>
  514.         /// Call XmlRawWriter.WriteEndElement() with prefix, local-name, and ns.
  515.         /// </summary>
  516.         public void WriteEndElementUnchecked(string prefix, string localName, string ns)
  517.         {
  518.             Debug.Assert(this.xstate == XmlState.EnumAttrs || this.xstate == XmlState.WithinContent, "WriteEndElement cannot be called in the " + this.xstate + " state.");
  519.             Writer.WriteEndElement(prefix, localName, ns);
  520.             this.xstate = XmlState.WithinContent;
  521.             this.depth--;
  522.             if (this.nsmgr != null)
  523.                 this.nsmgr.PopScope();
  524.         }
  525.        
  526.         /// <summary>
  527.         /// Call XmlRawWriter.WriteEndElement() with empty prefix, ns.
  528.         /// </summary>
  529.         public void WriteEndElementUnchecked(string localName)
  530.         {
  531.             WriteEndElementUnchecked(string.Empty, localName, string.Empty);
  532.         }
  533.        
  534.         /// <summary>
  535.         /// XmlRawWriter.WriteStartAttribute() with prefix, local-name, ns, and schema type.
  536.         /// </summary>
  537.         public void WriteStartAttributeUnchecked(string prefix, string localName, string ns)
  538.         {
  539.             Debug.Assert(this.xstate == XmlState.EnumAttrs, "WriteStartAttribute cannot be called in the " + this.xstate + " state.");
  540.             Writer.WriteStartAttribute(prefix, localName, ns);
  541.             this.xstate = XmlState.WithinAttr;
  542.             this.depth++;
  543.         }
  544.        
  545.         /// <summary>
  546.         /// XmlRawWriter.WriteStartAttribute() with empty prefix, ns, and null schema type.
  547.         /// </summary>
  548.         public void WriteStartAttributeUnchecked(string localName)
  549.         {
  550.             WriteStartAttributeUnchecked(string.Empty, localName, string.Empty);
  551.         }
  552.        
  553.         /// <summary>
  554.         /// XmlRawWriter.WriteEndAttribute().
  555.         /// </summary>
  556.         public void WriteEndAttributeUnchecked()
  557.         {
  558.             Debug.Assert(this.xstate == XmlState.WithinAttr, "WriteEndAttribute cannot be called in the " + this.xstate + " state.");
  559.             Writer.WriteEndAttribute();
  560.             this.xstate = XmlState.EnumAttrs;
  561.             this.depth--;
  562.         }
  563.        
  564.         /// <summary>
  565.         /// Add a new namespace declaration -- xmlns:prefix="ns" -- to the set of in-scope declarations.
  566.         /// NOTE: This method should only be called if caller can guarantee that the current state is EnumAttrs
  567.         /// and that there will be no namespace conflicts in the current scope (e.g. trying to map the
  568.         /// same prefix to different namespaces within the same element start tag). If no such
  569.         /// guarantees exist, then WriteNamespaceDeclaration() should be called instead.
  570.         /// </summary>
  571.         public void WriteNamespaceDeclarationUnchecked(string prefix, string ns)
  572.         {
  573.             Debug.Assert(prefix != null && ns != null);
  574.             Debug.Assert(this.xstate == XmlState.EnumAttrs, "WriteNamespaceDeclaration cannot be called in the " + this.xstate + " state.");
  575.            
  576.             // xmlns:foo="" is illegal
  577.             Debug.Assert(prefix.Length == 0 || ns.Length != 0);
  578.            
  579.             if (this.depth == 0) {
  580.                 // At top-level, so write namespace declaration directly to output
  581.                 Writer.WriteNamespaceDeclaration(prefix, ns);
  582.                 return;
  583.             }
  584.            
  585.             if (this.nsmgr == null) {
  586.                 // If namespace manager has no namespaces, then xmlns="" is in scope by default
  587.                 if (ns.Length == 0 && prefix.Length == 0)
  588.                     return;
  589.                
  590.                 this.nsmgr = new XmlNamespaceManager(this.runtime.NameTable);
  591.                 this.nsmgr.PushScope();
  592.             }
  593.            
  594.             if (this.nsmgr.LookupNamespace(prefix) != ns)
  595.                 AddNamespace(prefix, ns);
  596.         }
  597.        
  598.         /// <summary>
  599.         /// Write a text block to the XmlRawWriter.
  600.         /// </summary>
  601.         public void WriteStringUnchecked(string text)
  602.         {
  603.             Debug.Assert(this.xstate != XmlState.WithinSequence && this.xstate != XmlState.EnumAttrs, "WriteTextBlock cannot be called in the " + this.xstate + " state.");
  604.             Writer.WriteString(text);
  605.         }
  606.        
  607.         /// <summary>
  608.         /// Write a text block without escaping special characters.
  609.         /// </summary>
  610.         public void WriteRawUnchecked(string text)
  611.         {
  612.             Debug.Assert(this.xstate != XmlState.WithinSequence && this.xstate != XmlState.EnumAttrs, "WriteTextBlockNoEntities cannot be called in the " + this.xstate + " state.");
  613.             Writer.WriteRaw(text);
  614.         }
  615.        
  616.        
  617.         //-----------------------------------------------
  618.         // XmlQueryOutput methods
  619.         //-----------------------------------------------
  620.        
  621.         /// <summary>
  622.         /// Before calling XmlSequenceWriter.StartTree(), perform checks to ensure well-formedness.
  623.         /// </summary>
  624.         public void WriteStartRoot()
  625.         {
  626.             Debug.Assert(this.depth == 0, "Root node can only be constructed at top-level.");
  627.             if (this.xstate != XmlState.WithinSequence)
  628.                 ThrowInvalidStateError(XPathNodeType.Root);
  629.            
  630.             StartTree(XPathNodeType.Root);
  631.             this.depth++;
  632.         }
  633.        
  634.         /// <summary>
  635.         /// Call XmlSequenceWriter.EndTree() and reset state.
  636.         /// </summary>
  637.         public void WriteEndRoot()
  638.         {
  639.             Debug.Assert(this.depth == 1, "Root node can only be constructed at top-level.");
  640.             this.depth--;
  641.             EndTree();
  642.         }
  643.        
  644.         /// <summary>
  645.         /// WriteStartElement() with empty prefix, ns.
  646.         /// </summary>
  647.         public void WriteStartElementLocalName(string localName)
  648.         {
  649.             WriteStartElement(string.Empty, localName, string.Empty);
  650.         }
  651.        
  652.         /// <summary>
  653.         /// WriteStartAttribute() with empty prefix, ns, and null schema type.
  654.         /// </summary>
  655.         public void WriteStartAttributeLocalName(string localName)
  656.         {
  657.             WriteStartAttribute(string.Empty, localName, string.Empty);
  658.         }
  659.        
  660.         /// <summary>
  661.         /// Write an element with a name that is computed from a "prefix:localName" tag name and a set of prefix mappings.
  662.         /// </summary>
  663.         public void WriteStartElementComputed(string tagName, int prefixMappingsIndex)
  664.         {
  665.             WriteStartComputed(XPathNodeType.Element, tagName, prefixMappingsIndex);
  666.         }
  667.        
  668.         /// <summary>
  669.         /// Write an element with a name that is computed from a "prefix:localName" tag name and a namespace URI.
  670.         /// </summary>
  671.         public void WriteStartElementComputed(string tagName, string ns)
  672.         {
  673.             WriteStartComputed(XPathNodeType.Element, tagName, ns);
  674.         }
  675.        
  676.         /// <summary>
  677.         /// Write an element with a name that is copied from the navigator.
  678.         /// </summary>
  679.         public void WriteStartElementComputed(XPathNavigator navigator)
  680.         {
  681.             WriteStartComputed(XPathNodeType.Element, navigator);
  682.         }
  683.        
  684.         /// <summary>
  685.         /// Write an element with a name that is derived from the XmlQualifiedName.
  686.         /// </summary>
  687.         public void WriteStartElementComputed(XmlQualifiedName name)
  688.         {
  689.             WriteStartComputed(XPathNodeType.Element, name);
  690.         }
  691.        
  692.         /// <summary>
  693.         /// Write an attribute with a name that is computed from a "prefix:localName" tag name and a set of prefix mappings.
  694.         /// </summary>
  695.         public void WriteStartAttributeComputed(string tagName, int prefixMappingsIndex)
  696.         {
  697.             WriteStartComputed(XPathNodeType.Attribute, tagName, prefixMappingsIndex);
  698.         }
  699.        
  700.         /// <summary>
  701.         /// Write an attribute with a name that is computed from a "prefix:localName" tag name and a namespace URI.
  702.         /// </summary>
  703.         public void WriteStartAttributeComputed(string tagName, string ns)
  704.         {
  705.             WriteStartComputed(XPathNodeType.Attribute, tagName, ns);
  706.         }
  707.        
  708.         /// <summary>
  709.         /// Write an attribute with a name that is copied from the navigator.
  710.         /// </summary>
  711.         public void WriteStartAttributeComputed(XPathNavigator navigator)
  712.         {
  713.             WriteStartComputed(XPathNodeType.Attribute, navigator);
  714.         }
  715.        
  716.         /// <summary>
  717.         /// Write an attribute with a name that is derived from the XmlQualifiedName.
  718.         /// </summary>
  719.         public void WriteStartAttributeComputed(XmlQualifiedName name)
  720.         {
  721.             WriteStartComputed(XPathNodeType.Attribute, name);
  722.         }
  723.        
  724.         /// <summary>
  725.         /// Before calling XmlRawWriter.WriteNamespaceDeclaration(), perform various checks to ensure well-formedness.
  726.         /// </summary>
  727.         public void WriteNamespaceDeclaration(string prefix, string ns)
  728.         {
  729.             string nsExisting;
  730.             Debug.Assert(prefix != null && ns != null);
  731.            
  732.             ConstructInEnumAttrs(XPathNodeType.Namespace);
  733.            
  734.             if (this.nsmgr == null) {
  735.                 // If namespace manager has not yet been created, then there is no possibility of conflict
  736.                 WriteNamespaceDeclarationUnchecked(prefix, ns);
  737.             }
  738.             else {
  739.                 nsExisting = this.nsmgr.LookupNamespace(prefix);
  740.                 if (ns != nsExisting) {
  741.                     // prefix = "", ns = "", nsExisting --> Look for xmlns="", found xmlns="foo"
  742.                     // prefix = "", ns, nsExisting = null --> Look for xmlns="uri", no uri found
  743.                     // prefix = "", ns, nsExisting = "" --> Look for xmlns="uri", found xmlns=""
  744.                     // prefix = "", ns, nsExisting --> Look for xmlns="uri", found xmlns="uri2"
  745.                     // prefix, ns, nsExisting = null --> Look for xmlns:foo="uri", no uri found
  746.                     // prefix, ns, nsExisting --> Look for xmlns:foo="uri", found xmlns:foo="uri2"
  747.                    
  748.                     // If the prefix is mapped to a uri,
  749.                     if (nsExisting != null) {
  750.                         // Then throw an error except in a special case involving default namespace declaration
  751.                         // Looking for xmlns="", found xmlns="foo" -- ERROR
  752.                         // Looking for xmlns="uri", found xmlns="uri2" -- ERROR
  753.                         // Looking for xmlns="uri", found xmlns="" -- OK, unless xmlns="" declaration is
  754.                         // local or element's namespace is ""
  755.                         if (prefix.Length != 0 || nsExisting.Length != 0 || this.useDefNmsp)
  756.                             throw new XslTransformException(Res.XmlIl_NmspConflict, new string[] {prefix.Length == 0 ? "" : ":", prefix, ns, nsExisting});
  757.                     }
  758.                    
  759.                     // Add namespace to manager and write it to output
  760.                     AddNamespace(prefix, ns);
  761.                 }
  762.             }
  763.            
  764.             if (this.depth == 0)
  765.                 EndTree();
  766.         }
  767.        
  768.         /// <summary>
  769.         /// Before writing a namespace, perform various checks to ensure well-formedness.
  770.         /// </summary>
  771.         public void WriteStartNamespace(string prefix)
  772.         {
  773.             Debug.Assert(prefix != null, "Invalid argument");
  774.            
  775.             // Handle namespace attributes that are not sent directly to WriteNamespaceDeclaration
  776.             ConstructInEnumAttrs(XPathNodeType.Namespace);
  777.             this.nmspPrefix = prefix;
  778.             this.nmspText = string.Empty;
  779.            
  780.             this.xstate = XmlState.WithinNmsp;
  781.             this.depth++;
  782.         }
  783.        
  784.         /// <summary>
  785.         /// Cache the namespace's text.
  786.         /// </summary>
  787.         public void WriteNamespaceString(string text)
  788.         {
  789.             Debug.Assert(this.xstate == XmlState.WithinNmsp, "WriteNamespaceString cannot be called in the " + this.xstate + " state.");
  790.            
  791.             if (this.nmspText.Length == 0)
  792.                 this.nmspText = text;
  793.             else
  794.                 this.nmspText += text;
  795.         }
  796.        
  797.         /// <summary>
  798.         /// Before writing a namespace, perform various checks to ensure well-formedness.
  799.         /// </summary>
  800.         public void WriteEndNamespace()
  801.         {
  802.             Debug.Assert(this.xstate == XmlState.WithinNmsp, "WriteEndNamespace cannot be called in the " + this.xstate + " state.");
  803.            
  804.             this.xstate = XmlState.EnumAttrs;
  805.             this.depth--;
  806.            
  807.             // Write cached namespace attribute
  808.             WriteNamespaceDeclaration(this.nmspPrefix, this.nmspText);
  809.             this.nmspPrefix = null;
  810.            
  811.             if (this.depth == 0)
  812.                 EndTree();
  813.         }
  814.        
  815.         /// <summary>
  816.         /// Before writing a comment, perform various checks to ensure well-formedness.
  817.         /// </summary>
  818.         public void WriteStartComment()
  819.         {
  820.             // Xml state transitions
  821.             ConstructWithinContent(XPathNodeType.Comment);
  822.            
  823.             this.commentText = string.Empty;
  824.             this.xstate = XmlState.WithinComment;
  825.             this.depth++;
  826.         }
  827.        
  828.         /// <summary>
  829.         /// Cache the comment's text.
  830.         /// </summary>
  831.         public void WriteCommentString(string text)
  832.         {
  833.             Debug.Assert(this.xstate == XmlState.WithinComment, "WriteCommentString cannot be called in the " + this.xstate + " state.");
  834.            
  835.             if (this.commentText.Length == 0)
  836.                 this.commentText = text;
  837.             else
  838.                 this.commentText += text;
  839.         }
  840.        
  841.         /// <summary>
  842.         /// Before writing a comment, perform various checks to ensure well-formedness.
  843.         /// </summary>
  844.         public void WriteEndComment()
  845.         {
  846.             Debug.Assert(this.xstate == XmlState.WithinComment, "WriteEndComment cannot be called in the " + this.xstate + " state.");
  847.            
  848.             Writer.WriteComment(this.commentText);
  849.            
  850.             this.xstate = XmlState.WithinContent;
  851.             this.depth--;
  852.            
  853.             if (this.depth == 0)
  854.                 EndTree();
  855.         }
  856.        
  857.         /// <summary>
  858.         /// Before writing a processing instruction, perform various checks to ensure well-formedness.
  859.         /// </summary>
  860.         public void WriteStartProcessingInstruction(string target)
  861.         {
  862.             // Xml state transitions
  863.             ConstructWithinContent(XPathNodeType.ProcessingInstruction);
  864.            
  865.             // Verify PI name
  866.             ValidateNames.ValidateNameThrow("", target, "", XPathNodeType.ProcessingInstruction, ValidateNames.Flags.AllExceptPrefixMapping);
  867.            
  868.             this.piTarget = target;
  869.             this.piText = string.Empty;
  870.            
  871.             this.xstate = XmlState.WithinPI;
  872.             this.depth++;
  873.         }
  874.        
  875.         /// <summary>
  876.         /// Cache the processing instruction's text.
  877.         /// </summary>
  878.         public void WriteProcessingInstructionString(string text)
  879.         {
  880.             Debug.Assert(this.xstate == XmlState.WithinPI, "WriteProcessingInstructionString cannot be called in the " + this.xstate + " state.");
  881.            
  882.             if (this.piText.Length == 0)
  883.                 this.piText = text;
  884.             else
  885.                 this.piText += text;
  886.         }
  887.        
  888.         /// <summary>
  889.         /// Before writing a processing instruction, perform various checks to ensure well-formedness.
  890.         /// </summary>
  891.         public void WriteEndProcessingInstruction()
  892.         {
  893.             Debug.Assert(this.xstate == XmlState.WithinPI, "WriteEndProcessingInstruction cannot be called in the " + this.xstate + " state.");
  894.            
  895.             Writer.WriteProcessingInstruction(this.piTarget, this.piText);
  896.            
  897.             this.xstate = XmlState.WithinContent;
  898.             this.depth--;
  899.            
  900.             // Xml state transitions
  901.             if (this.depth == 0)
  902.                 EndTree();
  903.         }
  904.        
  905.         /// <summary>
  906.         /// Write an item to output. If currently constructing an Xml tree, then the item is always copied.
  907.         /// At the top-level, the item's identity is preserved unless it's an atomic value.
  908.         /// </summary>
  909.         public void WriteItem(XPathItem item)
  910.         {
  911.             if (item.IsNode) {
  912.                 XPathNavigator navigator = (XPathNavigator)item;
  913.                
  914.                 // If this is a top-level node, write a reference to it; else copy it by value
  915.                 if (this.xstate == XmlState.WithinSequence)
  916.                     this.seqwrt.WriteItem(navigator);
  917.                 else
  918.                     CopyNode(navigator);
  919.             }
  920.             else {
  921.                 // Call WriteItem for atomic values
  922.                 Debug.Assert(this.xstate == XmlState.WithinSequence, "Values can only be written at the top-level.");
  923.                 this.seqwrt.WriteItem(item);
  924.             }
  925.         }
  926.        
  927.         /// <summary>
  928.         /// Copy a node by value to output according to Xslt rules:
  929.         /// 1. Identity is never preserved
  930.         /// 2. If the item is an Rtf, preserve serialization hints when copying.
  931.         /// 3. If the item is a Root node, copy the children of the Root
  932.         /// </summary>
  933.         public void XsltCopyOf(XPathNavigator navigator)
  934.         {
  935.             RtfNavigator navRtf = navigator as RtfNavigator;
  936.            
  937.             if (navRtf != null) {
  938.                 // Copy Rtf
  939.                 navRtf.CopyToWriter(this);
  940.             }
  941.             else if (navigator.NodeType == XPathNodeType.Root) {
  942.                 // Copy children of root
  943.                 if (navigator.MoveToFirstChild()) {
  944.                     do {
  945.                         CopyNode(navigator);
  946.                     }
  947.                     while (navigator.MoveToNext());
  948.                    
  949.                     navigator.MoveToParent();
  950.                 }
  951.             }
  952.             else {
  953.                 // Copy node
  954.                 CopyNode(navigator);
  955.             }
  956.         }
  957.        
  958.         /// <summary>
  959.         /// Begin shallow copy of the navigator's current node to output. Returns true if EndCopy
  960.         /// should be called to complete the copy operation.
  961.         /// Automatically copies all in-scope namespaces on elements.
  962.         /// </summary>
  963.         public bool StartCopy(XPathNavigator navigator)
  964.         {
  965.             // StartDocument is a no-op
  966.             if (navigator.NodeType == XPathNodeType.Root)
  967.                 return true;
  968.            
  969.             if (StartCopy(navigator, true)) {
  970.                 Debug.Assert(navigator.NodeType == XPathNodeType.Element, "StartCopy should return true only for Element nodes.");
  971.                
  972.                 // Copy namespaces to output
  973.                 CopyNamespaces(navigator, XPathNamespaceScope.ExcludeXml);
  974.                
  975.                 return true;
  976.             }
  977.            
  978.             return false;
  979.         }
  980.        
  981.         /// <summary>
  982.         /// End shallow copy of the navigator's current node. Should be called only for Element and Document nodes.
  983.         /// </summary>
  984.         public void EndCopy(XPathNavigator navigator)
  985.         {
  986.             if (navigator.NodeType == XPathNodeType.Element)
  987.                 WriteEndElement();
  988.             else
  989.                 Debug.Assert(navigator.NodeType == XPathNodeType.Root, "EndCopy should only be called for Element and Document nodes.");
  990.         }
  991.        
  992.        
  993.         //-----------------------------------------------
  994.         // Helper methods
  995.         //-----------------------------------------------
  996.        
  997.         /// <summary>
  998.         /// Add an in-scope namespace.
  999.         /// </summary>
  1000.         private void AddNamespace(string prefix, string ns)
  1001.         {
  1002.             this.nsmgr.AddNamespace(prefix, ns);
  1003.             this.cntNmsp++;
  1004.             if (ns.Length == 0)
  1005.                 this.useDefNmsp = true;
  1006.         }
  1007.        
  1008.         /// <summary>
  1009.         /// Before writing text, perform various checks to ensure well-formedness.
  1010.         /// </summary>
  1011.         private void WriteString(string text, bool disableOutputEscaping)
  1012.         {
  1013.             Debug.Assert(text != null, "Invalid argument");
  1014.            
  1015.             // Xml state transitions
  1016.             switch (this.xstate) {
  1017.                 case XmlState.WithinSequence:
  1018.                     // Start constructing new tree
  1019.                     StartTree(XPathNodeType.Text);
  1020.                     goto case XmlState.WithinContent;
  1021.                     break;
  1022.                 case XmlState.WithinContent:
  1023.                    
  1024.                     if (disableOutputEscaping)
  1025.                         WriteRawUnchecked(text);
  1026.                     else
  1027.                         WriteStringUnchecked(text);
  1028.                     break;
  1029.                 case XmlState.EnumAttrs:
  1030.                    
  1031.                     // Enumerating attributes, so write text as element content
  1032.                     StartElementContentUnchecked();
  1033.                     goto case XmlState.WithinContent;
  1034.                     break;
  1035.                 case XmlState.WithinAttr:
  1036.                    
  1037.                     WriteStringUnchecked(text);
  1038.                     break;
  1039.                 case XmlState.WithinNmsp:
  1040.                    
  1041.                     WriteNamespaceString(text);
  1042.                     break;
  1043.                 case XmlState.WithinComment:
  1044.                    
  1045.                     // Comment text
  1046.                     WriteCommentString(text);
  1047.                     break;
  1048.                 case XmlState.WithinPI:
  1049.                    
  1050.                     // PI text
  1051.                     WriteProcessingInstructionString(text);
  1052.                     break;
  1053.                 default:
  1054.                    
  1055.                     Debug.Assert(false, "Text cannot be output in the " + this.xstate + " state.");
  1056.                     break;
  1057.             }
  1058.            
  1059.             if (this.depth == 0)
  1060.                 EndTree();
  1061.         }
  1062.        
  1063.         /// <summary>
  1064.         /// Deep copy the subtree that is rooted at this navigator's current position to output. If the current
  1065.         /// item is an element, copy all in-scope namespace nodes.
  1066.         /// </summary>
  1067.         private void CopyNode(XPathNavigator navigator)
  1068.         {
  1069.             XPathNodeType nodeType;
  1070.             int depthStart = this.depth;
  1071.             Debug.Assert(navigator != null);
  1072.            
  1073.             while (true) {
  1074.                 if (StartCopy(navigator, this.depth == depthStart)) {
  1075.                     nodeType = navigator.NodeType;
  1076.                     Debug.Assert(nodeType == XPathNodeType.Element, "StartCopy should return true only for Element nodes.");
  1077.                    
  1078.                     // Copy attributes
  1079.                     if (navigator.MoveToFirstAttribute()) {
  1080.                         do {
  1081.                             StartCopy(navigator, false);
  1082.                         }
  1083.                         while (navigator.MoveToNextAttribute());
  1084.                         navigator.MoveToParent();
  1085.                     }
  1086.                    
  1087.                     // Copy namespaces in document order (navigator returns them in reverse document order)
  1088.                     CopyNamespaces(navigator, (this.depth - 1 == depthStart) ? XPathNamespaceScope.ExcludeXml : XPathNamespaceScope.Local);
  1089.                    
  1090.                     StartElementContentUnchecked();
  1091.                    
  1092.                     // If children exist, move down to next level
  1093.                     if (navigator.MoveToFirstChild())
  1094.                         continue;
  1095.                    
  1096.                     EndCopy(navigator, (this.depth - 1) == depthStart);
  1097.                 }
  1098.                
  1099.                 // No children
  1100.                 while (true) {
  1101.                     if (this.depth == depthStart) {
  1102.                         // The entire subtree has been copied
  1103.                         return;
  1104.                     }
  1105.                    
  1106.                     if (navigator.MoveToNext()) {
  1107.                         // Found a sibling, so break to outer loop
  1108.                         break;
  1109.                     }
  1110.                    
  1111.                     // No siblings, so move up to previous level
  1112.                     navigator.MoveToParent();
  1113.                    
  1114.                     EndCopy(navigator, (this.depth - 1) == depthStart);
  1115.                 }
  1116.             }
  1117.         }
  1118.        
  1119.         /// <summary>
  1120.         /// Begin shallow copy of the navigator's current node to output. Returns true if EndCopy
  1121.         /// should be called to complete the copy operation.
  1122.         /// </summary>
  1123.         private bool StartCopy(XPathNavigator navigator, bool callChk)
  1124.         {
  1125.             bool mayHaveChildren = false;
  1126.            
  1127.             switch (navigator.NodeType) {
  1128.                 case XPathNodeType.Element:
  1129.                     // If checks need to be made, call XmlQueryOutput.WriteStartElement
  1130.                     if (callChk) {
  1131.                         WriteStartElement(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  1132.                     }
  1133.                     else {
  1134.                         WriteStartElementUnchecked(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  1135.                     }
  1136.                    
  1137.                     mayHaveChildren = true;
  1138.                     break;
  1139.                 case XPathNodeType.Attribute:
  1140.                    
  1141.                     // If checks need to be made, call XmlQueryOutput.WriteStartAttribute
  1142.                     if (callChk) {
  1143.                         WriteStartAttribute(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  1144.                     }
  1145.                     else {
  1146.                         WriteStartAttributeUnchecked(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  1147.                     }
  1148.                    
  1149.                     // Write attribute text
  1150.                     WriteString(navigator.Value);
  1151.                    
  1152.                     // If checks need to be made, call XmlQueryOutput.WriteEndAttribute
  1153.                     if (callChk) {
  1154.                         WriteEndAttribute();
  1155.                     }
  1156.                     else {
  1157.                         WriteEndAttributeUnchecked();
  1158.                     }
  1159.                     break;
  1160.                 case XPathNodeType.Namespace:
  1161.                    
  1162.                     // If checks need to be made, call XmlQueryOutput.WriteNamespaceDeclaration
  1163.                     if (callChk) {
  1164.                         // Do not allow namespaces to be copied after attributes
  1165.                         XmlAttributeCache attrCache = Writer as XmlAttributeCache;
  1166.                         if (attrCache != null && attrCache.Count != 0)
  1167.                             throw new XslTransformException(Res.XmlIl_NmspAfterAttr, string.Empty);
  1168.                        
  1169.                         WriteNamespaceDeclaration(navigator.LocalName, navigator.Value);
  1170.                     }
  1171.                     else {
  1172.                         WriteNamespaceDeclarationUnchecked(navigator.LocalName, navigator.Value);
  1173.                     }
  1174.                     break;
  1175.                 case XPathNodeType.Text:
  1176.                 case XPathNodeType.SignificantWhitespace:
  1177.                 case XPathNodeType.Whitespace:
  1178.                    
  1179.                     // If checks need to be made, call XmlQueryOutput.WriteString
  1180.                     if (callChk) {
  1181.                         WriteString(navigator.Value, false);
  1182.                     }
  1183.                     else {
  1184.                         // No flags are set, so this is simple element text (attributes, comments, pi's copy own text)
  1185.                         WriteStringUnchecked(navigator.Value);
  1186.                     }
  1187.                     break;
  1188.                 case XPathNodeType.Root:
  1189.                    
  1190.                     // Document node is invalid except at the top-level
  1191.                     Debug.Assert(this.xstate != XmlState.WithinSequence, "StartCopy should not called if state is WithinSequence");
  1192.                     ThrowInvalidStateError(XPathNodeType.Root);
  1193.                     break;
  1194.                 case XPathNodeType.Comment:
  1195.                    
  1196.                     WriteStartComment();
  1197.                     WriteCommentString(navigator.Value);
  1198.                     WriteEndComment();
  1199.                     break;
  1200.                 case XPathNodeType.ProcessingInstruction:
  1201.                    
  1202.                     WriteStartProcessingInstruction(navigator.LocalName);
  1203.                     WriteProcessingInstructionString(navigator.Value);
  1204.                     WriteEndProcessingInstruction();
  1205.                     break;
  1206.                 default:
  1207.                    
  1208.                     Debug.Assert(false);
  1209.                     break;
  1210.             }
  1211.            
  1212.             return mayHaveChildren;
  1213.         }
  1214.        
  1215.         /// <summary>
  1216.         /// End shallow copy of the navigator's current node to output. This method should only be called if StartCopy
  1217.         /// returned true.
  1218.         /// </summary>
  1219.         private void EndCopy(XPathNavigator navigator, bool callChk)
  1220.         {
  1221.             Debug.Assert(navigator.NodeType == XPathNodeType.Element);
  1222.             Debug.Assert(this.xstate == XmlState.WithinContent, "EndCopy cannot be called in the " + this.xstate + " state.");
  1223.            
  1224.             if (callChk)
  1225.                 WriteEndElement();
  1226.             else
  1227.                 WriteEndElementUnchecked(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  1228.         }
  1229.        
  1230.         /// <summary>
  1231.         /// Copy all namespaces of the specified type (in-scope, exclude-xml, local) in document order to output.
  1232.         /// </summary>
  1233.         private void CopyNamespaces(XPathNavigator navigator, XPathNamespaceScope nsScope)
  1234.         {
  1235.             Debug.Assert(navigator.NodeType == XPathNodeType.Element, "Only elements have namespaces to copy");
  1236.            
  1237.             // Default namespace undeclaration isn't included in navigator's namespace list, so add it now
  1238.             if (navigator.NamespaceURI.Length == 0) {
  1239.                 Debug.Assert(navigator.LocalName.Length != 0, "xmlns:foo='' isn't allowed");
  1240.                 WriteNamespaceDeclarationUnchecked(string.Empty, string.Empty);
  1241.             }
  1242.            
  1243.             // Since the namespace list is arranged in reverse-document order, recursively reverse it.
  1244.             if (navigator.MoveToFirstNamespace(nsScope)) {
  1245.                 CopyNamespacesHelper(navigator, nsScope);
  1246.                 navigator.MoveToParent();
  1247.             }
  1248.         }
  1249.        
  1250.         /// <summary>
  1251.         /// Recursive helper function that reverses order of the namespaces retrieved by MoveToFirstNamespace and
  1252.         /// MoveToNextNamespace.
  1253.         /// </summary>
  1254.         private void CopyNamespacesHelper(XPathNavigator navigator, XPathNamespaceScope nsScope)
  1255.         {
  1256.             string prefix = navigator.LocalName;
  1257.             string ns = navigator.Value;
  1258.            
  1259.             if (navigator.MoveToNextNamespace(nsScope))
  1260.                 CopyNamespacesHelper(navigator, nsScope);
  1261.            
  1262.             // No possibility for conflict, since we're copying namespaces from well-formed element
  1263.             WriteNamespaceDeclarationUnchecked(prefix, ns);
  1264.         }
  1265.        
  1266.         /// <summary>
  1267.         /// Ensure that state transitions to WithinContent.
  1268.         /// </summary>
  1269.         private void ConstructWithinContent(XPathNodeType rootType)
  1270.         {
  1271.             Debug.Assert(rootType == XPathNodeType.Element || rootType == XPathNodeType.Comment || rootType == XPathNodeType.ProcessingInstruction);
  1272.            
  1273.             switch (this.xstate) {
  1274.                 case XmlState.WithinSequence:
  1275.                     // If state is WithinSequence, call XmlSequenceWriter.StartTree
  1276.                     StartTree(rootType);
  1277.                     this.xstate = XmlState.WithinContent;
  1278.                     break;
  1279.                 case XmlState.WithinContent:
  1280.                    
  1281.                     // Already within element content
  1282.                     break;
  1283.                 case XmlState.EnumAttrs:
  1284.                    
  1285.                     // Start element content
  1286.                     StartElementContentUnchecked();
  1287.                     break;
  1288.                 default:
  1289.                    
  1290.                     // Construction is not allowed in this state
  1291.                     ThrowInvalidStateError(rootType);
  1292.                     break;
  1293.             }
  1294.         }
  1295.        
  1296.         /// <summary>
  1297.         /// Ensure that state transitions to EnumAttrs.
  1298.         /// </summary>
  1299.         private void ConstructInEnumAttrs(XPathNodeType rootType)
  1300.         {
  1301.             Debug.Assert(rootType == XPathNodeType.Attribute || rootType == XPathNodeType.Namespace);
  1302.            
  1303.             switch (this.xstate) {
  1304.                 case XmlState.WithinSequence:
  1305.                     StartTree(rootType);
  1306.                     this.xstate = XmlState.EnumAttrs;
  1307.                     break;
  1308.                 case XmlState.EnumAttrs:
  1309.                    
  1310.                     // Already in EnumAttrs state
  1311.                     break;
  1312.                 default:
  1313.                    
  1314.                     // Construction is not allowed in this state
  1315.                     ThrowInvalidStateError(rootType);
  1316.                     break;
  1317.             }
  1318.         }
  1319.        
  1320.         /// <summary>
  1321.         /// Namespace declarations are added to this.nsmgr. Just before element content has begun, write out
  1322.         /// all namespaces that were declared locally on the element.
  1323.         /// </summary>
  1324.         private void WriteCachedNamespaces()
  1325.         {
  1326.             string prefix;
  1327.             string ns;
  1328.            
  1329.             while (this.cntNmsp != 0) {
  1330.                 // Output each prefix->ns mapping pair
  1331.                 Debug.Assert(this.nsmgr != null);
  1332.                 this.cntNmsp--;
  1333.                 this.nsmgr.GetNamespaceDeclaration(this.cntNmsp, out prefix, out ns);
  1334.                 Writer.WriteNamespaceDeclaration(prefix, ns);
  1335.             }
  1336.         }
  1337.        
  1338.         /// <summary>
  1339.         /// Return the type of node that is under construction given the specified XmlState.
  1340.         /// </summary>
  1341.         private XPathNodeType XmlStateToNodeType(XmlState xstate)
  1342.         {
  1343.             switch (xstate) {
  1344.                 case XmlState.EnumAttrs:
  1345.                     return XPathNodeType.Element;
  1346.                 case XmlState.WithinContent:
  1347.                     return XPathNodeType.Element;
  1348.                 case XmlState.WithinAttr:
  1349.                     return XPathNodeType.Attribute;
  1350.                 case XmlState.WithinComment:
  1351.                     return XPathNodeType.Comment;
  1352.                 case XmlState.WithinPI:
  1353.                     return XPathNodeType.ProcessingInstruction;
  1354.             }
  1355.            
  1356.             Debug.Assert(false, xstate.ToString() + " is not a valid XmlState.");
  1357.             return XPathNodeType.Element;
  1358.         }
  1359.        
  1360.         /// <summary>
  1361.         /// If attribute's prefix conflicts with other prefixes then redeclare the prefix. If the prefix has
  1362.         /// not yet been declared, then add it to the namespace manager.
  1363.         /// </summary>
  1364.         private string CheckAttributePrefix(string prefix, string ns)
  1365.         {
  1366.             string nsExisting;
  1367.             Debug.Assert(prefix.Length != 0 && ns.Length != 0);
  1368.            
  1369.             // Ensure that this attribute's prefix does not conflict with previously declared prefixes in this scope
  1370.             if (this.nsmgr == null) {
  1371.                 // If namespace manager has no namespaces, then there is no possibility of conflict
  1372.                 WriteNamespaceDeclarationUnchecked(prefix, ns);
  1373.             }
  1374.             else {
  1375.                 while (true) {
  1376.                     // If prefix is already mapped to a different namespace,
  1377.                     nsExisting = this.nsmgr.LookupNamespace(prefix);
  1378.                     if (nsExisting != ns) {
  1379.                        
  1380.                         // Then if the prefix is already mapped,
  1381.                         if (nsExisting != null) {
  1382.                             // Then there is a conflict that must be resolved by finding another prefix
  1383.                             // Always find a new prefix, even if the conflict didn't occur in the current scope
  1384.                             // This decision allows more aggressive namespace analysis at compile-time
  1385.                             prefix = RemapPrefix(prefix, ns, false);
  1386.                             continue;
  1387.                         }
  1388.                        
  1389.                         // Add the mapping to the current scope
  1390.                         AddNamespace(prefix, ns);
  1391.                     }
  1392.                     break;
  1393.                 }
  1394.             }
  1395.            
  1396.             return prefix;
  1397.         }
  1398.        
  1399.         /// <summary>
  1400.         /// Remaps an element or attribute prefix using the following rules:
  1401.         ///
  1402.         /// 1. If another in-scope prefix is already mapped to "ns", then use that
  1403.         /// 2. Otherwise, if a prefix was previously mapped to "ns" by this method, then use that
  1404.         /// 3. Otherwise, generate a new prefix of the form 'xp_??', where ?? is a stringized counter
  1405.         ///
  1406.         /// These rules tend to reduce the number of unique prefixes used throughout the tree.
  1407.         /// </summary>
  1408.         private string RemapPrefix(string prefix, string ns, bool isElemPrefix)
  1409.         {
  1410.             const string PrefixFormat = "xp_{0}";
  1411.             string genPrefix;
  1412.             Debug.Assert(prefix != null && ns != null && ns.Length != 0);
  1413.            
  1414.             if (this.conflictPrefixes == null)
  1415.                 this.conflictPrefixes = new Hashtable(16);
  1416.            
  1417.             if (this.nsmgr == null) {
  1418.                 this.nsmgr = new XmlNamespaceManager(this.runtime.NameTable);
  1419.                 this.nsmgr.PushScope();
  1420.             }
  1421.            
  1422.             // Rule #1: If another in-scope prefix is already mapped to "ns", then use that
  1423.             genPrefix = this.nsmgr.LookupPrefix(ns);
  1424.             if (genPrefix != null) {
  1425.                 // Can't use an empty prefix for an attribute
  1426.                 if (isElemPrefix || genPrefix.Length != 0)
  1427.                     goto ReturnPrefix;
  1428.             }
  1429.            
  1430.             // Rule #2: Otherwise, if a prefix was previously mapped to "ns" by this method, then use that
  1431.             // Make sure that any previous prefix is different than "prefix"
  1432.             genPrefix = this.conflictPrefixes[ns] as string;
  1433.             if (genPrefix != null && genPrefix != prefix) {
  1434.                 // Can't use an empty prefix for an attribute
  1435.                 if (isElemPrefix || genPrefix.Length != 0)
  1436.                     goto ReturnPrefix;
  1437.             }
  1438.            
  1439.             // Rule #3: Otherwise, generate a new prefix of the form 'xp_??', where ?? is a stringized counter
  1440.             genPrefix = string.Format(CultureInfo.InvariantCulture, PrefixFormat, this.prefixIndex++);
  1441.             ReturnPrefix:
  1442.            
  1443.             // Save generated prefix so that it can be possibly be reused later
  1444.             this.conflictPrefixes[ns] = genPrefix;
  1445.            
  1446.             return genPrefix;
  1447.         }
  1448.        
  1449.         /// <summary>
  1450.         /// Write an element or attribute with a name that is computed from a "prefix:localName" tag name and a set of prefix mappings.
  1451.         /// </summary>
  1452.         private void WriteStartComputed(XPathNodeType nodeType, string tagName, int prefixMappingsIndex)
  1453.         {
  1454.             string prefix;
  1455.             string localName;
  1456.             string ns;
  1457.            
  1458.             // Parse the tag name and map the prefix to a namespace
  1459.             runtime.ParseTagName(tagName, prefixMappingsIndex, out prefix, out localName, out ns);
  1460.            
  1461.             // Validate the name parts
  1462.             prefix = EnsureValidName(prefix, localName, ns, nodeType);
  1463.            
  1464.             if (nodeType == XPathNodeType.Element)
  1465.                 WriteStartElement(prefix, localName, ns);
  1466.             else
  1467.                 WriteStartAttribute(prefix, localName, ns);
  1468.         }
  1469.        
  1470.         /// <summary>
  1471.         /// Write an element or attribute with a name that is computed from a "prefix:localName" tag name and a namespace URI.
  1472.         /// </summary>
  1473.         private void WriteStartComputed(XPathNodeType nodeType, string tagName, string ns)
  1474.         {
  1475.             string prefix;
  1476.             string localName;
  1477.            
  1478.             // Parse the tagName as a prefix, localName pair
  1479.             ValidateNames.ParseQNameThrow(tagName, out prefix, out localName);
  1480.            
  1481.             // Validate the name parts
  1482.             prefix = EnsureValidName(prefix, localName, ns, nodeType);
  1483.            
  1484.             if (nodeType == XPathNodeType.Element)
  1485.                 WriteStartElement(prefix, localName, ns);
  1486.             else
  1487.                 WriteStartAttribute(prefix, localName, ns);
  1488.         }
  1489.        
  1490.         /// <summary>
  1491.         /// Write an element or attribute with a name that is copied from the navigator.
  1492.         /// </summary>
  1493.         private void WriteStartComputed(XPathNodeType nodeType, XPathNavigator navigator)
  1494.         {
  1495.             string prefix;
  1496.             string localName;
  1497.             string ns;
  1498.            
  1499.             prefix = navigator.Prefix;
  1500.             localName = navigator.LocalName;
  1501.             ns = navigator.NamespaceURI;
  1502.            
  1503.             if (navigator.NodeType != nodeType) {
  1504.                 // Validate the name parts
  1505.                 prefix = EnsureValidName(prefix, localName, ns, nodeType);
  1506.             }
  1507.            
  1508.             if (nodeType == XPathNodeType.Element)
  1509.                 WriteStartElement(prefix, localName, ns);
  1510.             else
  1511.                 WriteStartAttribute(prefix, localName, ns);
  1512.         }
  1513.        
  1514.         /// <summary>
  1515.         /// Write an element or attribute with a name that is derived from the XmlQualifiedName.
  1516.         /// </summary>
  1517.         private void WriteStartComputed(XPathNodeType nodeType, XmlQualifiedName name)
  1518.         {
  1519.             string prefix;
  1520.             Debug.Assert(ValidateNames.ParseNCName(name.Name, 0) == name.Name.Length);
  1521.            
  1522.             // Validate the name parts
  1523.             prefix = (name.Namespace.Length != 0) ? RemapPrefix(string.Empty, name.Namespace, nodeType == XPathNodeType.Element) : string.Empty;
  1524.             prefix = EnsureValidName(prefix, name.Name, name.Namespace, nodeType);
  1525.            
  1526.             if (nodeType == XPathNodeType.Element)
  1527.                 WriteStartElement(prefix, name.Name, name.Namespace);
  1528.             else
  1529.                 WriteStartAttribute(prefix, name.Name, name.Namespace);
  1530.         }
  1531.        
  1532.         /// <summary>
  1533.         /// Ensure that the specified name parts are valid according to Xml 1.0 and Namespace 1.0 rules. Try to remap
  1534.         /// the prefix in order to attain validity. Throw if validity is not possible. Otherwise, return the (possibly
  1535.         /// remapped) prefix.
  1536.         /// </summary>
  1537.         private string EnsureValidName(string prefix, string localName, string ns, XPathNodeType nodeType)
  1538.         {
  1539.             if (!ValidateNames.ValidateName(prefix, localName, ns, nodeType, ValidateNames.Flags.AllExceptNCNames)) {
  1540.                 // Name parts are not valid as is. Try to re-map the prefix.
  1541.                 prefix = (ns.Length != 0) ? RemapPrefix(string.Empty, ns, nodeType == XPathNodeType.Element) : string.Empty;
  1542.                
  1543.                 ValidateNames.ValidateNameThrow(prefix, localName, ns, nodeType, ValidateNames.Flags.AllExceptNCNames);
  1544.             }
  1545.            
  1546.             return prefix;
  1547.         }
  1548.        
  1549.         /// <summary>
  1550.         /// Push element name parts onto the stack.
  1551.         /// </summary>
  1552.         private void PushElementNames(string prefix, string localName, string ns)
  1553.         {
  1554.             // Push the name parts onto a stack
  1555.             if (this.stkNames == null)
  1556.                 this.stkNames = new Stack();
  1557.            
  1558.             this.stkNames.Push(prefix);
  1559.             this.stkNames.Push(localName);
  1560.             this.stkNames.Push(ns);
  1561.         }
  1562.        
  1563.         /// <summary>
  1564.         /// Pop element name parts from the stack.
  1565.         /// </summary>
  1566.         private void PopElementNames(out string prefix, out string localName, out string ns)
  1567.         {
  1568.             Debug.Assert(this.stkNames != null);
  1569.            
  1570.             ns = this.stkNames.Pop() as string;
  1571.             localName = this.stkNames.Pop() as string;
  1572.             prefix = this.stkNames.Pop() as string;
  1573.         }
  1574.        
  1575.         /// <summary>
  1576.         /// Throw an invalid state transition error.
  1577.         /// </summary>
  1578.         private void ThrowInvalidStateError(XPathNodeType constructorType)
  1579.         {
  1580.             switch (constructorType) {
  1581.                 case XPathNodeType.Element:
  1582.                 case XPathNodeType.Root:
  1583.                 case XPathNodeType.Text:
  1584.                 case XPathNodeType.Comment:
  1585.                 case XPathNodeType.ProcessingInstruction:
  1586.                     throw new XslTransformException(Res.XmlIl_BadXmlState, new string[] {constructorType.ToString(), XmlStateToNodeType(this.xstate).ToString()});
  1587.                     break;
  1588.                 case XPathNodeType.Attribute:
  1589.                 case XPathNodeType.Namespace:
  1590.                    
  1591.                     if (this.depth == 1)
  1592.                         throw new XslTransformException(Res.XmlIl_BadXmlState, new string[] {constructorType.ToString(), this.rootType.ToString()});
  1593.                    
  1594.                     if (this.xstate == XmlState.WithinContent)
  1595.                         throw new XslTransformException(Res.XmlIl_BadXmlStateAttr, string.Empty);
  1596.                    
  1597.                     goto case XPathNodeType.Element;
  1598.                     break;
  1599.                 default:
  1600.                    
  1601.                     throw new XslTransformException(Res.XmlIl_BadXmlState, new string[] {"Unknown", XmlStateToNodeType(this.xstate).ToString()});
  1602.                     break;
  1603.             }
  1604.         }
  1605.     }
  1606. }

Developer Fusion