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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsdCachingReader.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.IO;
  16. using System.Text;
  17. using System.Xml.Schema;
  18. using System.Xml.XPath;
  19. using System.Diagnostics;
  20. using System.Globalization;
  21. using System.Collections;
  22. using System.Security.Policy;
  23. namespace System.Xml
  24. {
  25.    
  26.     internal class XsdCachingReader : XmlReader, IXmlLineInfo
  27.     {
  28.        
  29.         private enum CachingReaderState
  30.         {
  31.             None = 0,
  32.             Init = 1,
  33.             Record = 2,
  34.             Replay = 3,
  35.             ReaderClosed = 4,
  36.             Error = 5
  37.         }
  38.        
  39.         private XmlReader coreReader;
  40.         private XmlNameTable coreReaderNameTable;
  41.        
  42.         private ValidatingReaderNodeData[] contentEvents;
  43.         private ValidatingReaderNodeData[] attributeEvents;
  44.        
  45.         private ValidatingReaderNodeData cachedNode;
  46.        
  47.         private CachingReaderState cacheState;
  48.         int contentIndex;
  49.         int attributeCount;
  50.        
  51.         private CachingEventHandler cacheHandler;
  52.        
  53.         //current state
  54.         int currentAttrIndex;
  55.         int currentContentIndex;
  56.         bool readAhead;
  57.        
  58.         //Lineinfo
  59.         IXmlLineInfo lineInfo;
  60.        
  61.         //ReadAttributeValue TextNode
  62.         private ValidatingReaderNodeData textNode;
  63.        
  64.         //Constants
  65.         private const int InitialAttributeCount = 8;
  66.         private const int InitialContentCount = 4;
  67.        
  68.         //Constructor
  69.         internal XsdCachingReader(XmlReader reader, IXmlLineInfo lineInfo, CachingEventHandler handlerMethod)
  70.         {
  71.             this.coreReader = reader;
  72.             this.lineInfo = lineInfo;
  73.             this.cacheHandler = handlerMethod;
  74.             attributeEvents = new ValidatingReaderNodeData[InitialAttributeCount];
  75.             contentEvents = new ValidatingReaderNodeData[InitialContentCount];
  76.             Init();
  77.         }
  78.        
  79.         private void Init()
  80.         {
  81.             coreReaderNameTable = coreReader.NameTable;
  82.             cacheState = CachingReaderState.Init;
  83.             contentIndex = 0;
  84.             currentAttrIndex = -1;
  85.             currentContentIndex = -1;
  86.             attributeCount = 0;
  87.             cachedNode = null;
  88.             readAhead = false;
  89.             //Initialize the cachingReader with start state
  90.             if (coreReader.NodeType == XmlNodeType.Element) {
  91.                 ValidatingReaderNodeData element = AddContent(coreReader.NodeType);
  92.                 element.SetItemData(coreReader.LocalName, coreReader.Prefix, coreReader.NamespaceURI, coreReader.Depth);
  93.                 //Only created for element node type
  94.                 element.SetLineInfo(lineInfo);
  95.                 RecordAttributes();
  96.             }
  97.         }
  98.        
  99.         internal void Reset(XmlReader reader)
  100.         {
  101.             this.coreReader = reader;
  102.             Init();
  103.         }
  104.        
  105.         // Settings
  106.         public override XmlReaderSettings Settings {
  107.             get { return coreReader.Settings; }
  108.         }
  109.        
  110.         // Node Properties
  111.        
  112.         // Gets the type of the current node.
  113.         public override XmlNodeType NodeType {
  114.             get { return cachedNode.NodeType; }
  115.         }
  116.        
  117.         // Gets the name of the current node, including the namespace prefix.
  118.         public override string Name {
  119.             get { return cachedNode.GetAtomizedNameWPrefix(coreReaderNameTable); }
  120.         }
  121.        
  122.         // Gets the name of the current node without the namespace prefix.
  123.         public override string LocalName {
  124.             get { return cachedNode.LocalName; }
  125.         }
  126.        
  127.         // Gets the namespace URN (as defined in the W3C Namespace Specification) of the current namespace scope.
  128.         public override string NamespaceURI {
  129.             get { return cachedNode.Namespace; }
  130.         }
  131.        
  132.         // Gets the namespace prefix associated with the current node.
  133.         public override string Prefix {
  134.             get { return cachedNode.Prefix; }
  135.         }
  136.        
  137.         // Gets a value indicating whether the current node can have a non-empty Value.
  138.         public override bool HasValue {
  139.             get { return XmlReader.HasValueInternal(cachedNode.NodeType); }
  140.         }
  141.        
  142.         // Gets the text value of the current node.
  143.         public override string Value {
  144.             get { return cachedNode.RawValue; }
  145.         }
  146.        
  147.         // Gets the depth of the current node in the XML element stack.
  148.         public override int Depth {
  149.             get { return cachedNode.Depth; }
  150.         }
  151.        
  152.         // Gets the base URI of the current node.
  153.         public override string BaseURI {
  154.             get { return coreReader.BaseURI; }
  155.         }
  156.        
  157.         // Gets a value indicating whether the current node is an empty element (for example, <MyElement/>).
  158.         public override bool IsEmptyElement {
  159.             get { return false; }
  160.         }
  161.        
  162.         // Gets a value indicating whether the current node is an attribute that was generated from the default value defined
  163.         // in the DTD or schema.
  164.         public override bool IsDefault {
  165.             get { return false; }
  166.         }
  167.        
  168.         // Gets the quotation mark character used to enclose the value of an attribute node.
  169.         public override char QuoteChar {
  170.             get { return coreReader.QuoteChar; }
  171.         }
  172.        
  173.         // Gets the current xml:space scope.
  174.         public override XmlSpace XmlSpace {
  175.             get { return coreReader.XmlSpace; }
  176.         }
  177.        
  178.         // Gets the current xml:lang scope.
  179.         public override string XmlLang {
  180.             get { return coreReader.XmlLang; }
  181.         }
  182.        
  183.         // Attribute Accessors
  184.        
  185.         // The number of attributes on the current node.
  186.         public override int AttributeCount {
  187.             get { return attributeCount; }
  188.         }
  189.        
  190.         // Gets the value of the attribute with the specified Name.
  191.         public override string GetAttribute(string name)
  192.         {
  193.             int i;
  194.             if (name.IndexOf(':') == -1) {
  195.                 i = GetAttributeIndexWithoutPrefix(name);
  196.             }
  197.             else {
  198.                 i = GetAttributeIndexWithPrefix(name);
  199.             }
  200.             return (i >= 0) ? attributeEvents[i].RawValue : null;
  201.         }
  202.        
  203.         // Gets the value of the attribute with the specified LocalName and NamespaceURI.
  204.         public override string GetAttribute(string name, string namespaceURI)
  205.         {
  206.             namespaceURI = (namespaceURI == null) ? string.Empty : coreReaderNameTable.Get(namespaceURI);
  207.             name = coreReaderNameTable.Get(name);
  208.             ValidatingReaderNodeData attribute;
  209.             for (int i = 0; i < attributeCount; i++) {
  210.                 attribute = attributeEvents[i];
  211.                 if (Ref.Equal(attribute.LocalName, name) && Ref.Equal(attribute.Namespace, namespaceURI)) {
  212.                     return attribute.RawValue;
  213.                 }
  214.             }
  215.             return null;
  216.         }
  217.        
  218.         // Gets the value of the attribute with the specified index.
  219.         public override string GetAttribute(int i)
  220.         {
  221.             if (i < 0 || i >= attributeCount) {
  222.                 throw new ArgumentOutOfRangeException("i");
  223.             }
  224.             return attributeEvents[i].RawValue;
  225.         }
  226.        
  227.         // Gets the value of the attribute with the specified index.
  228.         public override string this[int i]
  229.         {
  230.             get { return GetAttribute(i); }
  231.         }
  232.        
  233.         // Gets the value of the attribute with the specified Name.
  234.         public override string this[string name]
  235.         {
  236.             get { return GetAttribute(name); }
  237.         }
  238.        
  239.         // Gets the value of the attribute with the specified LocalName and NamespaceURI.
  240.         public override string this[string name, string namespaceURI]
  241.         {
  242.             get { return GetAttribute(name, namespaceURI); }
  243.         }
  244.        
  245.         // Moves to the attribute with the specified Name.
  246.         public override bool MoveToAttribute(string name)
  247.         {
  248.             int i;
  249.             if (name.IndexOf(':') == -1) {
  250.                 i = GetAttributeIndexWithoutPrefix(name);
  251.             }
  252.             else {
  253.                 i = GetAttributeIndexWithPrefix(name);
  254.             }
  255.            
  256.             if (i >= 0) {
  257.                 currentAttrIndex = i;
  258.                 cachedNode = attributeEvents[i];
  259.                 return true;
  260.             }
  261.             else {
  262.                 return false;
  263.             }
  264.         }
  265.        
  266.        
  267.         // Moves to the attribute with the specified LocalName and NamespaceURI
  268.         public override bool MoveToAttribute(string name, string ns)
  269.         {
  270.             ns = (ns == null) ? string.Empty : coreReaderNameTable.Get(ns);
  271.             name = coreReaderNameTable.Get(name);
  272.             ValidatingReaderNodeData attribute;
  273.             for (int i = 0; i < attributeCount; i++) {
  274.                 attribute = attributeEvents[i];
  275.                 if (Ref.Equal(attribute.LocalName, name) && Ref.Equal(attribute.Namespace, ns)) {
  276.                     currentAttrIndex = i;
  277.                     cachedNode = attributeEvents[i];
  278.                     return true;
  279.                 }
  280.             }
  281.             return false;
  282.         }
  283.        
  284.         // Moves to the attribute with the specified index.
  285.         public override void MoveToAttribute(int i)
  286.         {
  287.             if (i < 0 || i >= attributeCount) {
  288.                 throw new ArgumentOutOfRangeException("i");
  289.             }
  290.             currentAttrIndex = i;
  291.             cachedNode = attributeEvents[i];
  292.         }
  293.        
  294.         // Moves to the first attribute.
  295.         public override bool MoveToFirstAttribute()
  296.         {
  297.             if (attributeCount == 0) {
  298.                 return false;
  299.             }
  300.             currentAttrIndex = 0;
  301.             cachedNode = attributeEvents[0];
  302.             return true;
  303.         }
  304.        
  305.         // Moves to the next attribute.
  306.         public override bool MoveToNextAttribute()
  307.         {
  308.             if (currentAttrIndex + 1 < attributeCount) {
  309.                 cachedNode = attributeEvents[++currentAttrIndex];
  310.                 return true;
  311.             }
  312.             return false;
  313.         }
  314.        
  315.         // Moves to the element that contains the current attribute node.
  316.         public override bool MoveToElement()
  317.         {
  318.             if (cacheState != CachingReaderState.Replay || cachedNode.NodeType != XmlNodeType.Attribute) {
  319.                 return false;
  320.             }
  321.             currentContentIndex = 0;
  322.             currentAttrIndex = -1;
  323.             Read();
  324.             return true;
  325.         }
  326.        
  327.         // Reads the next node from the stream/TextReader.
  328.         public override bool Read()
  329.         {
  330.             switch (cacheState) {
  331.                 case CachingReaderState.Init:
  332.                     cacheState = CachingReaderState.Record;
  333.                     goto case CachingReaderState.Record;
  334.                     break;
  335.                 case CachingReaderState.Record:
  336.                    
  337.                     ValidatingReaderNodeData recordedNode = null;
  338.                     if (coreReader.Read()) {
  339.                         switch (coreReader.NodeType) {
  340.                             case XmlNodeType.Element:
  341.                                 //Dont record element within the content of a union type since the main reader will break on this and the underlying coreReader will be positioned on this node
  342.                                 cacheState = CachingReaderState.ReaderClosed;
  343.                                 return false;
  344.                             case XmlNodeType.EndElement:
  345.                                
  346.                                 recordedNode = AddContent(coreReader.NodeType);
  347.                                 recordedNode.SetItemData(coreReader.LocalName, coreReader.Prefix, coreReader.NamespaceURI, coreReader.Depth);
  348.                                 //Only created for element node type
  349.                                 recordedNode.SetLineInfo(lineInfo);
  350.                                 break;
  351.                             case XmlNodeType.Comment:
  352.                             case XmlNodeType.ProcessingInstruction:
  353.                             case XmlNodeType.Text:
  354.                             case XmlNodeType.Whitespace:
  355.                             case XmlNodeType.SignificantWhitespace:
  356.                                
  357.                                 recordedNode = AddContent(coreReader.NodeType);
  358.                                 recordedNode.SetItemData(coreReader.Value);
  359.                                 recordedNode.SetLineInfo(lineInfo);
  360.                                 recordedNode.Depth = coreReader.Depth;
  361.                                 break;
  362.                             default:
  363.                                
  364.                                 break;
  365.                         }
  366.                         cachedNode = recordedNode;
  367.                         return true;
  368.                     }
  369.                     else {
  370.                         cacheState = CachingReaderState.ReaderClosed;
  371.                         return false;
  372.                     }
  373.                     break;
  374.                 case CachingReaderState.Replay:
  375.                    
  376.                     if (currentContentIndex >= contentIndex) {
  377.                         //When positioned on the last cached node, switch back as the underlying coreReader is still positioned on this node
  378.                         cacheState = CachingReaderState.ReaderClosed;
  379.                         cacheHandler(this);
  380.                         if (coreReader.NodeType != XmlNodeType.Element || readAhead) {
  381.                             //Only when coreReader not positioned on Element node, read ahead, otherwise it is on the next element node already, since this was not cached
  382.                             return coreReader.Read();
  383.                         }
  384.                         return true;
  385.                     }
  386.                     cachedNode = contentEvents[currentContentIndex];
  387.                     if (currentContentIndex > 0) {
  388.                         ClearAttributesInfo();
  389.                     }
  390.                     currentContentIndex++;
  391.                     return true;
  392.                 default:
  393.                    
  394.                     return false;
  395.             }
  396.         }
  397.        
  398.         internal ValidatingReaderNodeData RecordTextNode(string textValue, int depth, int lineNo, int linePos)
  399.         {
  400.             ValidatingReaderNodeData textNode = AddContent(XmlNodeType.Text);
  401.             textNode.SetItemData(textValue);
  402.             textNode.SetLineInfo(lineNo, linePos);
  403.             textNode.Depth = depth;
  404.             return textNode;
  405.         }
  406.        
  407.         internal void SwitchTextNodeAndEndElement(string textValue)
  408.         {
  409.             Debug.Assert(coreReader.NodeType == XmlNodeType.EndElement || (coreReader.NodeType == XmlNodeType.Element && coreReader.IsEmptyElement));
  410.            
  411.             ValidatingReaderNodeData textNode = RecordTextNode(textValue, coreReader.Depth + 1, 0, 0);
  412.             int endElementIndex = contentIndex - 2;
  413.             ValidatingReaderNodeData endElementNode = contentEvents[endElementIndex];
  414.             Debug.Assert(endElementNode.NodeType == XmlNodeType.EndElement);
  415.             contentEvents[endElementIndex] = textNode;
  416.             contentEvents[contentIndex - 1] = endElementNode;
  417.         }
  418.        
  419.         internal void RecordEndElementNode()
  420.         {
  421.             ValidatingReaderNodeData recordedNode = AddContent(XmlNodeType.EndElement);
  422.             Debug.Assert(coreReader.NodeType == XmlNodeType.EndElement || (coreReader.NodeType == XmlNodeType.Element && coreReader.IsEmptyElement));
  423.             recordedNode.SetItemData(coreReader.LocalName, coreReader.Prefix, coreReader.NamespaceURI, coreReader.Depth);
  424.             recordedNode.SetLineInfo(coreReader as IXmlLineInfo);
  425.             if (coreReader.IsEmptyElement) {
  426.                 //Simulated endElement node for <e/>, the coreReader is on cached Element node itself.
  427.                 readAhead = true;
  428.             }
  429.         }
  430.        
  431.         // Gets a value indicating whether XmlReader is positioned at the end of the stream.
  432.         public override bool EOF {
  433.             get { return cacheState == CachingReaderState.ReaderClosed && coreReader.EOF; }
  434.         }
  435.        
  436.         // Closes the stream, changes the ReadState to Closed, and sets all the properties back to zero.
  437.         public override void Close()
  438.         {
  439.             coreReader.Close();
  440.             cacheState = CachingReaderState.ReaderClosed;
  441.         }
  442.        
  443.         // Returns the read state of the stream.
  444.         public override ReadState ReadState {
  445.             get { return coreReader.ReadState; }
  446.         }
  447.        
  448.         // Skips to the end tag of the current element.
  449.         public override void Skip()
  450.         {
  451.             //Skip on caching reader should move to the end of the subtree, past all cached events
  452.             switch (cachedNode.NodeType) {
  453.                 case XmlNodeType.Element:
  454.                     if (coreReader.NodeType != XmlNodeType.EndElement && !readAhead) {
  455.                         //will be true for IsDefault cases where we peek only one node ahead
  456.                         int startDepth = coreReader.Depth - 1;
  457.                         while (coreReader.Read() && coreReader.Depth > startDepth)
  458.                             ;
  459.                     }
  460.                     coreReader.Read();
  461.                     cacheState = CachingReaderState.ReaderClosed;
  462.                     cacheHandler(this);
  463.                     break;
  464.                 case XmlNodeType.Attribute:
  465.                    
  466.                     MoveToElement();
  467.                     goto case XmlNodeType.Element;
  468.                     break;
  469.                 default:
  470.                    
  471.                     Debug.Assert(cacheState == CachingReaderState.Replay);
  472.                     Read();
  473.                     break;
  474.             }
  475.         }
  476.        
  477.         // Gets the XmlNameTable associated with this implementation.
  478.         public override XmlNameTable NameTable {
  479.             get { return coreReaderNameTable; }
  480.         }
  481.        
  482.         // Resolves a namespace prefix in the current element's scope.
  483.         public override string LookupNamespace(string prefix)
  484.         {
  485.             return coreReader.LookupNamespace(prefix);
  486.         }
  487.        
  488.         // Resolves the entity reference for nodes of NodeType EntityReference.
  489.         public override void ResolveEntity()
  490.         {
  491.             throw new InvalidOperationException();
  492.         }
  493.        
  494.         // Parses the attribute value into one or more Text and/or EntityReference node types.
  495.         public override bool ReadAttributeValue()
  496.         {
  497.             Debug.Assert(cacheState == CachingReaderState.Replay);
  498.             if (cachedNode.NodeType != XmlNodeType.Attribute) {
  499.                 return false;
  500.             }
  501.             cachedNode = CreateDummyTextNode(cachedNode.RawValue, cachedNode.Depth + 1);
  502.             return true;
  503.         }
  504.        
  505.         //
  506.         // IXmlLineInfo members
  507.         //
  508.        
  509.         bool IXmlLineInfo.HasLineInfo()
  510.         {
  511.             return true;
  512.         }
  513.        
  514.         int IXmlLineInfo.LineNumber {
  515.             get { return cachedNode.LineNumber; }
  516.         }
  517.        
  518.         int IXmlLineInfo.LinePosition {
  519.             get { return cachedNode.LinePosition; }
  520.         }
  521.        
  522.         //Private methods
  523.         internal void SetToReplayMode()
  524.         {
  525.             cacheState = CachingReaderState.Replay;
  526.             currentContentIndex = 0;
  527.             currentAttrIndex = -1;
  528.             Read();
  529.             //Position on first node recorded to begin replaying
  530.         }
  531.        
  532.         internal XmlReader GetCoreReader()
  533.         {
  534.             return coreReader;
  535.         }
  536.        
  537.         internal IXmlLineInfo GetLineInfo()
  538.         {
  539.             return lineInfo;
  540.         }
  541.        
  542.         private void ClearAttributesInfo()
  543.         {
  544.             attributeCount = 0;
  545.             currentAttrIndex = -1;
  546.         }
  547.        
  548.         private ValidatingReaderNodeData AddAttribute(int attIndex)
  549.         {
  550.             Debug.Assert(attIndex <= attributeEvents.Length);
  551.             ValidatingReaderNodeData attInfo = attributeEvents[attIndex];
  552.             if (attInfo != null) {
  553.                 attInfo.Clear(XmlNodeType.Attribute);
  554.                 return attInfo;
  555.             }
  556.             if (attIndex >= attributeEvents.Length - 1) {
  557.                 //reached capacity of array, Need to increase capacity to twice the initial
  558.                 ValidatingReaderNodeData[] newAttributeEvents = new ValidatingReaderNodeData[attributeEvents.Length * 2];
  559.                 Array.Copy(attributeEvents, 0, newAttributeEvents, 0, attributeEvents.Length);
  560.                 attributeEvents = newAttributeEvents;
  561.             }
  562.             attInfo = attributeEvents[attIndex];
  563.             if (attInfo == null) {
  564.                 attInfo = new ValidatingReaderNodeData(XmlNodeType.Attribute);
  565.                 attributeEvents[attIndex] = attInfo;
  566.             }
  567.             return attInfo;
  568.         }
  569.        
  570.         private ValidatingReaderNodeData AddContent(XmlNodeType nodeType)
  571.         {
  572.             Debug.Assert(contentIndex <= contentEvents.Length);
  573.             ValidatingReaderNodeData contentInfo = contentEvents[contentIndex];
  574.             if (contentInfo != null) {
  575.                 contentInfo.Clear(nodeType);
  576.                 contentIndex++;
  577.                 return contentInfo;
  578.             }
  579.             if (contentIndex >= contentEvents.Length - 1) {
  580.                 //reached capacity of array, Need to increase capacity to twice the initial
  581.                 ValidatingReaderNodeData[] newContentEvents = new ValidatingReaderNodeData[contentEvents.Length * 2];
  582.                 Array.Copy(contentEvents, 0, newContentEvents, 0, contentEvents.Length);
  583.                 contentEvents = newContentEvents;
  584.             }
  585.             contentInfo = contentEvents[contentIndex];
  586.             if (contentInfo == null) {
  587.                 contentInfo = new ValidatingReaderNodeData(nodeType);
  588.                 contentEvents[contentIndex] = contentInfo;
  589.             }
  590.             contentIndex++;
  591.             return contentInfo;
  592.         }
  593.        
  594.         private void RecordAttributes()
  595.         {
  596.             Debug.Assert(coreReader.NodeType == XmlNodeType.Element);
  597.             ValidatingReaderNodeData attInfo;
  598.             attributeCount = coreReader.AttributeCount;
  599.             if (coreReader.MoveToFirstAttribute()) {
  600.                 int attIndex = 0;
  601.                 do {
  602.                     attInfo = AddAttribute(attIndex);
  603.                     attInfo.SetItemData(coreReader.LocalName, coreReader.Prefix, coreReader.NamespaceURI, coreReader.Depth);
  604.                     attInfo.SetLineInfo(lineInfo);
  605.                     attInfo.RawValue = coreReader.Value;
  606.                     attIndex++;
  607.                 }
  608.                 while (coreReader.MoveToNextAttribute());
  609.                 coreReader.MoveToElement();
  610.             }
  611.         }
  612.        
  613.         private int GetAttributeIndexWithoutPrefix(string name)
  614.         {
  615.             name = coreReaderNameTable.Get(name);
  616.             if (name == null) {
  617.                 return -1;
  618.             }
  619.             ValidatingReaderNodeData attribute;
  620.             for (int i = 0; i < attributeCount; i++) {
  621.                 attribute = attributeEvents[i];
  622.                 if (Ref.Equal(attribute.LocalName, name) && attribute.Prefix.Length == 0) {
  623.                     return i;
  624.                 }
  625.             }
  626.             return -1;
  627.         }
  628.        
  629.         private int GetAttributeIndexWithPrefix(string name)
  630.         {
  631.             name = coreReaderNameTable.Get(name);
  632.             if (name == null) {
  633.                 return -1;
  634.             }
  635.             ValidatingReaderNodeData attribute;
  636.             for (int i = 0; i < attributeCount; i++) {
  637.                 attribute = attributeEvents[i];
  638.                 if (Ref.Equal(attribute.GetAtomizedNameWPrefix(coreReaderNameTable), name)) {
  639.                     return i;
  640.                 }
  641.             }
  642.             return -1;
  643.         }
  644.        
  645.         private ValidatingReaderNodeData CreateDummyTextNode(string attributeValue, int depth)
  646.         {
  647.             if (textNode == null) {
  648.                 textNode = new ValidatingReaderNodeData(XmlNodeType.Text);
  649.             }
  650.             textNode.Depth = depth;
  651.             textNode.RawValue = attributeValue;
  652.             return textNode;
  653.         }
  654.     }
  655. }

Developer Fusion