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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlSequenceWriter.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.Generic;
  17. using System.Diagnostics;
  18. using System.Xml;
  19. using System.Xml.XPath;
  20. using System.Xml.Schema;
  21. namespace System.Xml.Xsl.Runtime
  22. {
  23.     using Res = System.Xml.Utils.Res;
  24.    
  25.     /// <summary>
  26.     /// External XmlWriter Cached Sequence
  27.     /// ===================================================================================================
  28.     /// Multiple Trees Merged into Entity Multiple Trees
  29.     ///
  30.     /// Attributes Error Floating
  31.     /// at top-level Attribute
  32.     ///
  33.     /// Namespace Error Floating
  34.     /// at top-level Namespace
  35.     ///
  36.     /// Elements, Text, PI Implicit Root Floating
  37.     /// Comments at top-level Nodes
  38.     ///
  39.     /// Root at top-level Ignored Root
  40.     ///
  41.     /// Atomic Values Whitespace-Separated Atomic Values
  42.     /// at top-level Text Node
  43.     ///
  44.     /// Nodes By Reference Copied Preserve Identity
  45.     /// </summary>
  46.     internal abstract class XmlSequenceWriter
  47.     {
  48.         /// <summary>
  49.         /// Start construction of a new Xml tree (document or fragment).
  50.         /// </summary>
  51.         public abstract XmlRawWriter StartTree(XPathNodeType rootType, IXmlNamespaceResolver nsResolver, XmlNameTable nameTable);
  52.        
  53.         /// <summary>
  54.         /// End construction of a new Xml tree (document or fragment).
  55.         /// </summary>
  56.         public abstract void EndTree();
  57.        
  58.         /// <summary>
  59.         /// Write a top-level item by reference.
  60.         /// </summary>
  61.         public abstract void WriteItem(XPathItem item);
  62.     }
  63.    
  64.    
  65.     /// <summary>
  66.     /// An implementation of XmlSequenceWriter that builds a cached XPath/XQuery sequence.
  67.     /// </summary>
  68.     internal class XmlCachedSequenceWriter : XmlSequenceWriter
  69.     {
  70.         private XmlQueryItemSequence seqTyped;
  71.         private XPathDocument doc;
  72.         private XmlRawWriter writer;
  73.        
  74.         /// <summary>
  75.         /// Constructor.
  76.         /// </summary>
  77.         public XmlCachedSequenceWriter()
  78.         {
  79.             this.seqTyped = new XmlQueryItemSequence();
  80.         }
  81.        
  82.         /// <summary>
  83.         /// Return the sequence after it has been fully constructed.
  84.         /// </summary>
  85.         public XmlQueryItemSequence ResultSequence {
  86.             get { return this.seqTyped; }
  87.         }
  88.        
  89.         /// <summary>
  90.         /// Start construction of a new Xml tree (document or fragment).
  91.         /// </summary>
  92.         public override XmlRawWriter StartTree(XPathNodeType rootType, IXmlNamespaceResolver nsResolver, XmlNameTable nameTable)
  93.         {
  94.             // Build XPathDocument
  95.             // If rootType != XPathNodeType.Root, then build an XQuery fragment
  96.             this.doc = new XPathDocument(nameTable);
  97.             this.writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames | (rootType == XPathNodeType.Root ? XPathDocument.LoadFlags.None : XPathDocument.LoadFlags.Fragment), string.Empty);
  98.             this.writer.NamespaceResolver = nsResolver;
  99.             return this.writer;
  100.         }
  101.        
  102.         /// <summary>
  103.         /// End construction of a new Xml tree (document or fragment).
  104.         /// </summary>
  105.         public override void EndTree()
  106.         {
  107.             // Add newly constructed document to sequence
  108.             this.writer.Close();
  109.             this.seqTyped.Add(this.doc.CreateNavigator());
  110.         }
  111.        
  112.         /// <summary>
  113.         /// Write a top-level item by reference.
  114.         /// </summary>
  115.         public override void WriteItem(XPathItem item)
  116.         {
  117.             // Preserve identity
  118.             this.seqTyped.AddClone(item);
  119.         }
  120.     }
  121.    
  122.    
  123.     /// <summary>
  124.     /// An implementation of XmlSequenceWriter that converts an instance of the XQuery data model into a series
  125.     /// of calls to XmlRawWriter. The algorithm to do this is designed to be compatible with the rules in the
  126.     /// "XSLT 2.0 and XQuery 1.0 Serialization" spec. Here are the rules we use:
  127.     /// 1. An exception is thrown if the top-level sequence contains attribute or namespace nodes
  128.     /// 2. Each atomic value in the top-level sequence is converted to text, and XmlWriter.WriteString is called
  129.     /// 3. A call to XmlRawWriter.WriteWhitespace(" ") is made between adjacent atomic values at the top-level
  130.     /// 4. All items in the top-level sequence are merged together into a single result document.
  131.     /// </summary>
  132.     internal class XmlMergeSequenceWriter : XmlSequenceWriter
  133.     {
  134.         private XmlRawWriter xwrt;
  135.         private bool lastItemWasAtomic;
  136.        
  137.         /// <summary>
  138.         /// Constructor.
  139.         /// </summary>
  140.         public XmlMergeSequenceWriter(XmlRawWriter xwrt)
  141.         {
  142.             this.xwrt = xwrt;
  143.             this.lastItemWasAtomic = false;
  144.         }
  145.        
  146.         /// <summary>
  147.         /// Start construction of a new Xml tree (document or fragment).
  148.         /// </summary>
  149.         public override XmlRawWriter StartTree(XPathNodeType rootType, IXmlNamespaceResolver nsResolver, XmlNameTable nameTable)
  150.         {
  151.             if (rootType == XPathNodeType.Attribute || rootType == XPathNodeType.Namespace)
  152.                 throw new XslTransformException(Res.XmlIl_TopLevelAttrNmsp, string.Empty);
  153.            
  154.             // Provide a namespace resolver to the writer
  155.             this.xwrt.NamespaceResolver = nsResolver;
  156.            
  157.             return this.xwrt;
  158.         }
  159.        
  160.         /// <summary>
  161.         /// End construction of a new Xml tree (document or fragment).
  162.         /// </summary>
  163.         public override void EndTree()
  164.         {
  165.             this.lastItemWasAtomic = false;
  166.         }
  167.        
  168.         /// <summary>
  169.         /// Write a top-level item by reference.
  170.         /// </summary>
  171.         public override void WriteItem(XPathItem item)
  172.         {
  173.             if (item.IsNode) {
  174.                 XPathNavigator nav = item as XPathNavigator;
  175.                
  176.                 if (nav.NodeType == XPathNodeType.Attribute || nav.NodeType == XPathNodeType.Namespace)
  177.                     throw new XslTransformException(Res.XmlIl_TopLevelAttrNmsp, string.Empty);
  178.                
  179.                 // Copy navigator to raw writer
  180.                 CopyNode(nav);
  181.                 this.lastItemWasAtomic = false;
  182.             }
  183.             else {
  184.                 WriteString(item.Value);
  185.             }
  186.         }
  187.        
  188.         /// <summary>
  189.         /// Write the string value of a top-level atomic value.
  190.         /// </summary>
  191.         private void WriteString(string value)
  192.         {
  193.             if (this.lastItemWasAtomic) {
  194.                 // Insert space character between adjacent atomic values
  195.                 this.xwrt.WriteWhitespace(" ");
  196.             }
  197.             else {
  198.                 this.lastItemWasAtomic = true;
  199.             }
  200.             this.xwrt.WriteString(value);
  201.         }
  202.        
  203.         /// <summary>
  204.         /// Copy the navigator subtree to the raw writer.
  205.         /// </summary>
  206.         private void CopyNode(XPathNavigator nav)
  207.         {
  208.             XPathNodeType nodeType;
  209.             int iLevel = 0;
  210.            
  211.             while (true) {
  212.                 if (CopyShallowNode(nav)) {
  213.                     nodeType = nav.NodeType;
  214.                     if (nodeType == XPathNodeType.Element) {
  215.                         // Copy attributes
  216.                         if (nav.MoveToFirstAttribute()) {
  217.                             do {
  218.                                 CopyShallowNode(nav);
  219.                             }
  220.                             while (nav.MoveToNextAttribute());
  221.                             nav.MoveToParent();
  222.                         }
  223.                        
  224.                         // Copy namespaces in document order (navigator returns them in reverse document order)
  225.                         XPathNamespaceScope nsScope = (iLevel == 0) ? XPathNamespaceScope.ExcludeXml : XPathNamespaceScope.Local;
  226.                         if (nav.MoveToFirstNamespace(nsScope)) {
  227.                             CopyNamespaces(nav, nsScope);
  228.                             nav.MoveToParent();
  229.                         }
  230.                        
  231.                         this.xwrt.StartElementContent();
  232.                     }
  233.                    
  234.                     // If children exist, move down to next level
  235.                     if (nav.MoveToFirstChild()) {
  236.                         iLevel++;
  237.                         continue;
  238.                     }
  239.                     else {
  240.                         // EndElement
  241.                         if (nav.NodeType == XPathNodeType.Element)
  242.                             this.xwrt.WriteEndElement(nav.Prefix, nav.LocalName, nav.NamespaceURI);
  243.                     }
  244.                 }
  245.                
  246.                 // No children
  247.                 while (true) {
  248.                     if (iLevel == 0) {
  249.                         // The entire subtree has been copied
  250.                         return;
  251.                     }
  252.                    
  253.                     if (nav.MoveToNext()) {
  254.                         // Found a sibling, so break to outer loop
  255.                         break;
  256.                     }
  257.                    
  258.                     // No siblings, so move up to previous level
  259.                     iLevel--;
  260.                     nav.MoveToParent();
  261.                    
  262.                     // EndElement
  263.                     if (nav.NodeType == XPathNodeType.Element)
  264.                         this.xwrt.WriteFullEndElement(nav.Prefix, nav.LocalName, nav.NamespaceURI);
  265.                 }
  266.             }
  267.         }
  268.        
  269.         /// <summary>
  270.         /// Begin shallow copy of the specified node to the writer. Returns true if the node might have content.
  271.         /// </summary>
  272.         private bool CopyShallowNode(XPathNavigator nav)
  273.         {
  274.             bool mayHaveChildren = false;
  275.            
  276.             switch (nav.NodeType) {
  277.                 case XPathNodeType.Element:
  278.                     this.xwrt.WriteStartElement(nav.Prefix, nav.LocalName, nav.NamespaceURI);
  279.                     mayHaveChildren = true;
  280.                     break;
  281.                 case XPathNodeType.Attribute:
  282.                    
  283.                     this.xwrt.WriteStartAttribute(nav.Prefix, nav.LocalName, nav.NamespaceURI);
  284.                     this.xwrt.WriteString(nav.Value);
  285.                     this.xwrt.WriteEndAttribute();
  286.                     break;
  287.                 case XPathNodeType.Text:
  288.                    
  289.                     this.xwrt.WriteString(nav.Value);
  290.                     break;
  291.                 case XPathNodeType.SignificantWhitespace:
  292.                 case XPathNodeType.Whitespace:
  293.                    
  294.                     this.xwrt.WriteWhitespace(nav.Value);
  295.                     break;
  296.                 case XPathNodeType.Root:
  297.                    
  298.                     mayHaveChildren = true;
  299.                     break;
  300.                 case XPathNodeType.Comment:
  301.                    
  302.                     this.xwrt.WriteComment(nav.Value);
  303.                     break;
  304.                 case XPathNodeType.ProcessingInstruction:
  305.                    
  306.                     this.xwrt.WriteProcessingInstruction(nav.LocalName, nav.Value);
  307.                     break;
  308.                 case XPathNodeType.Namespace:
  309.                    
  310.                     this.xwrt.WriteNamespaceDeclaration(nav.LocalName, nav.Value);
  311.                     break;
  312.                 default:
  313.                    
  314.                     Debug.Assert(false);
  315.                     break;
  316.             }
  317.            
  318.             return mayHaveChildren;
  319.         }
  320.        
  321.         /// <summary>
  322.         /// Copy all or some (which depends on nsScope) of the namespaces on the navigator's current node to the
  323.         /// raw writer.
  324.         /// </summary>
  325.         private void CopyNamespaces(XPathNavigator nav, XPathNamespaceScope nsScope)
  326.         {
  327.             string prefix = nav.LocalName;
  328.             string ns = nav.Value;
  329.            
  330.             if (nav.MoveToNextNamespace(nsScope)) {
  331.                 CopyNamespaces(nav, nsScope);
  332.             }
  333.            
  334.             this.xwrt.WriteNamespaceDeclaration(prefix, ns);
  335.         }
  336.     }
  337. }

Developer Fusion