The Labs \ Source Viewer \ SSCLI \ System.Xml.XPath \ LoadFlags

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XPathDocument.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.IO;
  17. using System.Xml;
  18. using System.Xml.Schema;
  19. using System.Collections.Generic;
  20. using MS.Internal.Xml.Cache;
  21. using System.Diagnostics;
  22. using System.Text;
  23. namespace System.Xml.XPath
  24. {
  25.    
  26.     /// <summary>
  27.     /// XDocument follows the XPath/XQuery data model. All nodes in the tree reference the document,
  28.     /// and the document references the root node of the tree. All namespaces are stored out-of-line,
  29.     /// in an Element --> In-Scope-Namespaces map.
  30.     /// </summary>
  31.     public class XPathDocument : IXPathNavigable
  32.     {
  33.         private XPathNode[] pageText, pageRoot, pageXmlNmsp;
  34.         private int idxText, idxRoot, idxXmlNmsp;
  35.         private XmlNameTable nameTable;
  36.         private bool hasLineInfo;
  37.         private Dictionary<XPathNodeRef, XPathNodeRef> mapNmsp;
  38.         private Dictionary<string, XPathNodeRef> idValueMap;
  39.        
  40.         /// <summary>
  41.         /// Flags that control Load behavior.
  42.         /// </summary>
  43.         internal enum LoadFlags
  44.         {
  45.             None = 0,
  46.             AtomizeNames = 1,
  47.             // Do not assume that names passed to XPathDocumentBuilder have been pre-atomized, and atomize them
  48.             Fragment = 2
  49.             // Create a document with no document node
  50.         }
  51.        
  52.        
  53.         //-----------------------------------------------
  54.         // Creation Methods
  55.         //-----------------------------------------------
  56.        
  57.         /// <summary>
  58.         /// Create a new empty document.
  59.         /// </summary>
  60.         internal XPathDocument()
  61.         {
  62.             this.nameTable = new NameTable();
  63.         }
  64.        
  65.         /// <summary>
  66.         /// Create a new empty document. All names should be atomized using "nameTable".
  67.         /// </summary>
  68.         internal XPathDocument(XmlNameTable nameTable)
  69.         {
  70.             if (nameTable == null)
  71.                 throw new ArgumentNullException("nameTable");
  72.            
  73.             this.nameTable = nameTable;
  74.         }
  75.        
  76.         /// <summary>
  77.         /// Create a new document and load the content from the reader.
  78.         /// </summary>
  79.         public XPathDocument(XmlReader reader) : this(reader, XmlSpace.Default)
  80.         {
  81.         }
  82.        
  83.         /// <summary>
  84.         /// Create a new document from "reader", with whitespace handling controlled according to "space".
  85.         /// </summary>
  86.         public XPathDocument(XmlReader reader, XmlSpace space)
  87.         {
  88.             if (reader == null)
  89.                 throw new ArgumentNullException("reader");
  90.            
  91.             LoadFromReader(reader, space);
  92.         }
  93.        
  94.         /// <summary>
  95.         /// Create a new document and load the content from the text reader.
  96.         /// </summary>
  97.         public XPathDocument(TextReader textReader)
  98.         {
  99.             XmlTextReaderImpl reader = SetupReader(new XmlTextReaderImpl(string.Empty, textReader));
  100.            
  101.             try {
  102.                 LoadFromReader(reader, XmlSpace.Default);
  103.             }
  104.             finally {
  105.                 reader.Close();
  106.             }
  107.         }
  108.        
  109.         /// <summary>
  110.         /// Create a new document and load the content from the stream.
  111.         /// </summary>
  112.         public XPathDocument(Stream stream)
  113.         {
  114.             XmlTextReaderImpl reader = SetupReader(new XmlTextReaderImpl(string.Empty, stream));
  115.            
  116.             try {
  117.                 LoadFromReader(reader, XmlSpace.Default);
  118.             }
  119.             finally {
  120.                 reader.Close();
  121.             }
  122.         }
  123.        
  124.         /// <summary>
  125.         /// Create a new document and load the content from the Uri.
  126.         /// </summary>
  127.         public XPathDocument(string uri) : this(uri, XmlSpace.Default)
  128.         {
  129.         }
  130.        
  131.         /// <summary>
  132.         /// Create a new document and load the content from the Uri, with whitespace handling controlled according to "space".
  133.         /// </summary>
  134.         public XPathDocument(string uri, XmlSpace space)
  135.         {
  136.             XmlTextReaderImpl reader = SetupReader(new XmlTextReaderImpl(uri));
  137.            
  138.             try {
  139.                 LoadFromReader(reader, space);
  140.             }
  141.             finally {
  142.                 reader.Close();
  143.             }
  144.         }
  145.        
  146.         /// <summary>
  147.         /// Create a writer that can be used to create nodes in this document. The root node will be assigned "baseUri", and flags
  148.         /// can be passed to indicate that names should be atomized by the builder and/or a fragment should be created.
  149.         /// </summary>
  150.         internal XmlRawWriter LoadFromWriter(LoadFlags flags, string baseUri)
  151.         {
  152.             return new XPathDocumentBuilder(this, null, baseUri, flags);
  153.         }
  154.        
  155.         /// <summary>
  156.         /// Create a writer that can be used to create nodes in this document. The root node will be assigned "baseUri", and flags
  157.         /// can be passed to indicate that names should be atomized by the builder and/or a fragment should be created.
  158.         /// </summary>
  159.         internal void LoadFromReader(XmlReader reader, XmlSpace space)
  160.         {
  161.             XPathDocumentBuilder builder;
  162.             IXmlLineInfo lineInfo;
  163.             string xmlnsUri;
  164.             bool topLevelReader;
  165.             int initialDepth;
  166.            
  167.             if (reader == null)
  168.                 throw new ArgumentNullException("reader");
  169.            
  170.             // Determine line number provider
  171.             lineInfo = reader as IXmlLineInfo;
  172.             if (lineInfo == null || !lineInfo.HasLineInfo())
  173.                 lineInfo = null;
  174.             this.hasLineInfo = (lineInfo != null);
  175.            
  176.             this.nameTable = reader.NameTable;
  177.             builder = new XPathDocumentBuilder(this, lineInfo, reader.BaseURI, LoadFlags.None);
  178.            
  179.             try {
  180.                 // Determine whether reader is in initial state
  181.                 topLevelReader = (reader.ReadState == ReadState.Initial);
  182.                 initialDepth = reader.Depth;
  183.                
  184.                 // Get atomized xmlns uri
  185.                 Debug.Assert((object)this.nameTable.Get(string.Empty) == (object)string.Empty, "NameTable must contain atomized string.Empty");
  186.                 xmlnsUri = this.nameTable.Get("http://www.w3.org/2000/xmlns/");
  187.                
  188.                 // Read past Initial state; if there are no more events then load is complete
  189.                 if (topLevelReader && !reader.Read())
  190.                     return;
  191.                
  192.                 // Read all events
  193.                 do {
  194.                     // If reader began in intermediate state, return when all siblings have been read
  195.                     if (!topLevelReader && reader.Depth < initialDepth)
  196.                         return;
  197.                    
  198.                     switch (reader.NodeType) {
  199.                         case XmlNodeType.Element:
  200.                            
  201.                             {
  202.                                 bool isEmptyElement = reader.IsEmptyElement;
  203.                                
  204.                                 builder.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI, reader.BaseURI);
  205.                                
  206.                                 // Add attribute and namespace nodes to element
  207.                                 while (reader.MoveToNextAttribute()) {
  208.                                     string namespaceUri = reader.NamespaceURI;
  209.                                    
  210.                                     if ((object)namespaceUri == (object)xmlnsUri) {
  211.                                         if (reader.Prefix.Length == 0) {
  212.                                             // Default namespace declaration "xmlns"
  213.                                             Debug.Assert(reader.LocalName == "xmlns");
  214.                                             builder.WriteNamespaceDeclaration(string.Empty, reader.Value);
  215.                                         }
  216.                                         else {
  217.                                             Debug.Assert(reader.Prefix == "xmlns");
  218.                                             builder.WriteNamespaceDeclaration(reader.LocalName, reader.Value);
  219.                                         }
  220.                                     }
  221.                                     else {
  222.                                         builder.WriteStartAttribute(reader.Prefix, reader.LocalName, namespaceUri);
  223.                                         builder.WriteString(reader.Value, TextBlockType.Text);
  224.                                         builder.WriteEndAttribute();
  225.                                     }
  226.                                 }
  227.                                
  228.                                 if (isEmptyElement)
  229.                                     builder.WriteEndElement(true);
  230.                                 break;
  231.                             }
  232.                             break;
  233.                         case XmlNodeType.EndElement:
  234.                            
  235.                             builder.WriteEndElement(false);
  236.                             break;
  237.                         case XmlNodeType.Text:
  238.                         case XmlNodeType.CDATA:
  239.                            
  240.                             builder.WriteString(reader.Value, TextBlockType.Text);
  241.                             break;
  242.                         case XmlNodeType.SignificantWhitespace:
  243.                            
  244.                             if (reader.XmlSpace == XmlSpace.Preserve)
  245.                                 builder.WriteString(reader.Value, TextBlockType.SignificantWhitespace);
  246.                             else
  247.                                 // Significant whitespace without xml:space="preserve" is not significant in XPath/XQuery data model
  248.                                 goto case XmlNodeType.Whitespace;
  249.                             break;
  250.                         case XmlNodeType.Whitespace:
  251.                            
  252.                             Debug.Assert(reader.XmlSpace != XmlSpace.Preserve);
  253.                            
  254.                             // Always filter top-level whitespace
  255.                             if (space == XmlSpace.Preserve && (!topLevelReader || reader.Depth != 0))
  256.                                 builder.WriteString(reader.Value, TextBlockType.Whitespace);
  257.                             break;
  258.                         case XmlNodeType.Comment:
  259.                            
  260.                             builder.WriteComment(reader.Value);
  261.                             break;
  262.                         case XmlNodeType.ProcessingInstruction:
  263.                            
  264.                             builder.WriteProcessingInstruction(reader.LocalName, reader.Value, reader.BaseURI);
  265.                             break;
  266.                         case XmlNodeType.EntityReference:
  267.                            
  268.                             reader.ResolveEntity();
  269.                             break;
  270.                         case XmlNodeType.DocumentType:
  271.                            
  272.                             // Create ID tables
  273.                             SchemaInfo info = XmlReader.GetDtdSchemaInfo(reader);
  274.                             if (info != null)
  275.                                 builder.CreateIdTables(info);
  276.                             break;
  277.                         case XmlNodeType.EndEntity:
  278.                         case XmlNodeType.None:
  279.                         case XmlNodeType.XmlDeclaration:
  280.                            
  281.                             break;
  282.                     }
  283.                 }
  284.                 while (reader.Read());
  285.             }
  286.             finally {
  287.                 builder.Close();
  288.             }
  289.         }
  290.        
  291.         /// <summary>
  292.         /// Create a navigator positioned on the root node of the document.
  293.         /// </summary>
  294.         public XPathNavigator CreateNavigator()
  295.         {
  296.             return new XPathDocumentNavigator(this.pageRoot, this.idxRoot, null, 0);
  297.         }
  298.        
  299.        
  300.         //-----------------------------------------------
  301.         // Document Properties
  302.         //-----------------------------------------------
  303.        
  304.         /// <summary>
  305.         /// Return the name table used to atomize all name parts (local name, namespace uri, prefix).
  306.         /// </summary>
  307.         internal XmlNameTable NameTable {
  308.             get { return this.nameTable; }
  309.         }
  310.        
  311.         /// <summary>
  312.         /// Return true if line number information is recorded in the cache.
  313.         /// </summary>
  314.         internal bool HasLineInfo {
  315.             get { return this.hasLineInfo; }
  316.         }
  317.        
  318.         /// <summary>
  319.         /// Return the singleton collapsed text node associated with the document. One physical text node
  320.         /// represents each logical text node in the document that is the only content-typed child of its
  321.         /// element parent.
  322.         /// </summary>
  323.         internal int GetCollapsedTextNode(out XPathNode[] pageText)
  324.         {
  325.             pageText = this.pageText;
  326.             return this.idxText;
  327.         }
  328.        
  329.         /// <summary>
  330.         /// Set the page and index where the singleton collapsed text node is stored.
  331.         /// </summary>
  332.         internal void SetCollapsedTextNode(XPathNode[] pageText, int idxText)
  333.         {
  334.             this.pageText = pageText;
  335.             this.idxText = idxText;
  336.         }
  337.        
  338.         /// <summary>
  339.         /// Return the root node of the document. This may not be a node of type XPathNodeType.Root if this
  340.         /// is a document fragment.
  341.         /// </summary>
  342.         internal int GetRootNode(out XPathNode[] pageRoot)
  343.         {
  344.             pageRoot = this.pageRoot;
  345.             return this.idxRoot;
  346.         }
  347.        
  348.         /// <summary>
  349.         /// Set the page and index where the root node is stored.
  350.         /// </summary>
  351.         internal void SetRootNode(XPathNode[] pageRoot, int idxRoot)
  352.         {
  353.             this.pageRoot = pageRoot;
  354.             this.idxRoot = idxRoot;
  355.         }
  356.        
  357.         /// <summary>
  358.         /// Every document has an implicit xmlns:xml namespace node.
  359.         /// </summary>
  360.         internal int GetXmlNamespaceNode(out XPathNode[] pageXmlNmsp)
  361.         {
  362.             pageXmlNmsp = this.pageXmlNmsp;
  363.             return this.idxXmlNmsp;
  364.         }
  365.        
  366.         /// <summary>
  367.         /// Set the page and index where the implicit xmlns:xml node is stored.
  368.         /// </summary>
  369.         internal void SetXmlNamespaceNode(XPathNode[] pageXmlNmsp, int idxXmlNmsp)
  370.         {
  371.             this.pageXmlNmsp = pageXmlNmsp;
  372.             this.idxXmlNmsp = idxXmlNmsp;
  373.         }
  374.        
  375.         /// <summary>
  376.         /// Associate a namespace node with an element.
  377.         /// </summary>
  378.         internal void AddNamespace(XPathNode[] pageElem, int idxElem, XPathNode[] pageNmsp, int idxNmsp)
  379.         {
  380.             Debug.Assert(pageElem[idxElem].NodeType == XPathNodeType.Element && pageNmsp[idxNmsp].NodeType == XPathNodeType.Namespace);
  381.            
  382.             if (this.mapNmsp == null)
  383.                 this.mapNmsp = new Dictionary<XPathNodeRef, XPathNodeRef>();
  384.            
  385.             this.mapNmsp.Add(new XPathNodeRef(pageElem, idxElem), new XPathNodeRef(pageNmsp, idxNmsp));
  386.         }
  387.        
  388.         /// <summary>
  389.         /// Lookup the namespace nodes associated with an element.
  390.         /// </summary>
  391.         internal int LookupNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[] pageNmsp)
  392.         {
  393.             XPathNodeRef nodeRef = new XPathNodeRef(pageElem, idxElem);
  394.             Debug.Assert(pageElem[idxElem].NodeType == XPathNodeType.Element);
  395.            
  396.             // Check whether this element has any local namespaces
  397.             if (this.mapNmsp == null || !this.mapNmsp.ContainsKey(nodeRef)) {
  398.                 pageNmsp = null;
  399.                 return 0;
  400.             }
  401.            
  402.             // Yes, so return the page and index of the first local namespace node
  403.             nodeRef = this.mapNmsp[nodeRef];
  404.            
  405.             pageNmsp = nodeRef.Page;
  406.             return nodeRef.Index;
  407.         }
  408.        
  409.         /// <summary>
  410.         /// Add an element indexed by ID value.
  411.         /// </summary>
  412.         internal void AddIdElement(string id, XPathNode[] pageElem, int idxElem)
  413.         {
  414.             if (this.idValueMap == null)
  415.                 this.idValueMap = new Dictionary<string, XPathNodeRef>();
  416.            
  417.             if (!this.idValueMap.ContainsKey(id))
  418.                 this.idValueMap.Add(id, new XPathNodeRef(pageElem, idxElem));
  419.         }
  420.        
  421.         /// <summary>
  422.         /// Lookup the element node associated with the specified ID value.
  423.         /// </summary>
  424.         internal int LookupIdElement(string id, out XPathNode[] pageElem)
  425.         {
  426.             XPathNodeRef nodeRef;
  427.            
  428.             if (this.idValueMap == null || !this.idValueMap.ContainsKey(id)) {
  429.                 pageElem = null;
  430.                 return 0;
  431.             }
  432.            
  433.             // Extract page and index from XPathNodeRef
  434.             nodeRef = this.idValueMap[id];
  435.             pageElem = nodeRef.Page;
  436.             return nodeRef.Index;
  437.         }
  438.        
  439.        
  440.         //-----------------------------------------------
  441.         // Helper Methods
  442.         //-----------------------------------------------
  443.        
  444.         /// <summary>
  445.         /// Set properties on the reader so that it is backwards-compatible with V1.
  446.         /// </summary>
  447.         private XmlTextReaderImpl SetupReader(XmlTextReaderImpl reader)
  448.         {
  449.             reader.EntityHandling = EntityHandling.ExpandEntities;
  450.             reader.XmlValidatingReaderCompatibilityMode = true;
  451.             return reader;
  452.         }
  453.     }
  454. }

Developer Fusion