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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlElementList.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. namespace System.Xml
  16. {
  17.     using System;
  18.     using System.Collections;
  19.     using System.Diagnostics;
  20.    
  21.     internal class XmlElementList : XmlNodeList
  22.     {
  23.         string asterisk;
  24.         int changeCount;
  25.         //recording the total number that the dom tree has been changed ( insertion and deletetion )
  26.         //the member vars below are saved for further reconstruction
  27.         string name;
  28.         //only one of 2 string groups will be initialized depends on which constructor is called.
  29.         string localName;
  30.         string namespaceURI;
  31.         XmlNode rootNode;
  32.         // the memeber vars belwo serves the optimization of accessing of the elements in the list
  33.         int curInd;
  34.         // -1 means the starting point for a new search round
  35.         XmlNode curElem;
  36.         // if sets to rootNode, means the starting point for a new search round
  37.         bool empty;
  38.         // whether the list is empty
  39.         bool atomized;
  40.         //whether the localname and namespaceuri are aomized
  41.         int matchCount;
  42.         // cached list count. -1 means it needs reconstruction
  43.         private XmlElementList(XmlNode parent)
  44.         {
  45.             Debug.Assert(parent != null);
  46.             Debug.Assert(parent.NodeType == XmlNodeType.Element || parent.NodeType == XmlNodeType.Document);
  47.             this.rootNode = parent;
  48.             Debug.Assert(parent.Document != null);
  49.             this.curInd = -1;
  50.             this.curElem = rootNode;
  51.             this.changeCount = 0;
  52.             this.empty = false;
  53.             this.atomized = true;
  54.             this.matchCount = -1;
  55.             new XmlElementListListener(parent.Document, this);
  56.         }
  57.        
  58.         internal void ConcurrencyCheck(XmlNodeChangedEventArgs args)
  59.         {
  60.             if (atomized == false) {
  61.                 XmlNameTable nameTable = this.rootNode.Document.NameTable;
  62.                 this.localName = nameTable.Add(this.localName);
  63.                 this.namespaceURI = nameTable.Add(this.namespaceURI);
  64.                 this.atomized = true;
  65.             }
  66.             if (IsMatch(args.Node)) {
  67.                 this.changeCount++;
  68.                 this.curInd = -1;
  69.                 this.curElem = rootNode;
  70.                 if (args.Action == XmlNodeChangedAction.Insert)
  71.                     this.empty = false;
  72.             }
  73.             this.matchCount = -1;
  74.         }
  75.        
  76.         internal XmlElementList(XmlNode parent, string name) : this(parent)
  77.         {
  78.             Debug.Assert(parent.Document != null);
  79.             XmlNameTable nt = parent.Document.NameTable;
  80.             Debug.Assert(nt != null);
  81.             asterisk = nt.Add("*");
  82.             this.name = nt.Add(name);
  83.             this.localName = null;
  84.             this.namespaceURI = null;
  85.         }
  86.        
  87.         internal XmlElementList(XmlNode parent, string localName, string namespaceURI) : this(parent)
  88.         {
  89.             Debug.Assert(parent.Document != null);
  90.             XmlNameTable nt = parent.Document.NameTable;
  91.             Debug.Assert(nt != null);
  92.             asterisk = nt.Add("*");
  93.             this.localName = nt.Get(localName);
  94.             this.namespaceURI = nt.Get(namespaceURI);
  95.             if ((this.localName == null) || (this.namespaceURI == null)) {
  96.                 this.empty = true;
  97.                 this.atomized = false;
  98.                 this.localName = localName;
  99.                 this.namespaceURI = namespaceURI;
  100.             }
  101.             this.name = null;
  102.         }
  103.        
  104.         internal int ChangeCount {
  105.             get { return changeCount; }
  106.         }
  107.        
  108.         // return the next element node that is in PreOrder
  109.         private XmlNode NextElemInPreOrder(XmlNode curNode)
  110.         {
  111.             Debug.Assert(curNode != null);
  112.             //For preorder walking, first try its child
  113.             XmlNode retNode = curNode.FirstChild;
  114.             if (retNode == null) {
  115.                 //if no child, the next node forward will the be the NextSibling of the first ancestor which has NextSibling
  116.                 //so, first while-loop find out such an ancestor (until no more ancestor or the ancestor is the rootNode
  117.                 retNode = curNode;
  118.                 while (retNode != null && retNode != rootNode && retNode.NextSibling == null) {
  119.                     retNode = retNode.ParentNode;
  120.                 }
  121.                 //then if such ancestor exists, set the retNode to its NextSibling
  122.                 if (retNode != null && retNode != rootNode)
  123.                     retNode = retNode.NextSibling;
  124.             }
  125.             if (retNode == this.rootNode)
  126.                 retNode = null;
  127.             return retNode;
  128.         }
  129.        
  130.         // return the previous element node that is in PreOrder
  131.         private XmlNode PrevElemInPreOrder(XmlNode curNode)
  132.         {
  133.             Debug.Assert(curNode != null);
  134.             //For preorder walking, the previous node will be the right-most node in the tree of PreviousSibling of the curNode
  135.             XmlNode retNode = curNode.PreviousSibling;
  136.             // so if the PreviousSibling is not null, going through the tree down to find the right-most node
  137.             while (retNode != null) {
  138.                 if (retNode.LastChild == null)
  139.                     break;
  140.                 retNode = retNode.LastChild;
  141.             }
  142.             // if no PreviousSibling, the previous node will be the curNode's parentNode
  143.             if (retNode == null)
  144.                 retNode = curNode.ParentNode;
  145.             if (retNode == this.rootNode)
  146.                 retNode = null;
  147.             return retNode;
  148.         }
  149.        
  150.         // if the current node a matching element node
  151.         private bool IsMatch(XmlNode curNode)
  152.         {
  153.             if (curNode.NodeType == XmlNodeType.Element) {
  154.                 if (this.name != null) {
  155.                     if (Ref.Equal(this.name, asterisk) || Ref.Equal(curNode.Name, this.name))
  156.                         return true;
  157.                 }
  158.                 else {
  159.                     if ((Ref.Equal(this.localName, asterisk) || Ref.Equal(curNode.LocalName, this.localName)) && (Ref.Equal(this.namespaceURI, asterisk) || curNode.NamespaceURI == this.namespaceURI)) {
  160.                         return true;
  161.                     }
  162.                 }
  163.             }
  164.             return false;
  165.         }
  166.        
  167.         private XmlNode GetMatchingNode(XmlNode n, bool bNext)
  168.         {
  169.             Debug.Assert(n != null);
  170.             XmlNode node = n;
  171.             do {
  172.                 if (bNext)
  173.                     node = NextElemInPreOrder(node);
  174.                 else
  175.                     node = PrevElemInPreOrder(node);
  176.             }
  177.             while (node != null && !IsMatch(node));
  178.             return node;
  179.         }
  180.        
  181.         private XmlNode GetNthMatchingNode(XmlNode n, bool bNext, int nCount)
  182.         {
  183.             Debug.Assert(n != null);
  184.             XmlNode node = n;
  185.             for (int ind = 0; ind < nCount; ind++) {
  186.                 node = GetMatchingNode(node, bNext);
  187.                 if (node == null)
  188.                     return null;
  189.             }
  190.             return node;
  191.         }
  192.        
  193.         //the function is for the enumerator to find out the next available matching element node
  194.         public XmlNode GetNextNode(XmlNode n)
  195.         {
  196.             if (this.empty == true)
  197.                 return null;
  198.             XmlNode node = (n == null) ? rootNode : n;
  199.             return GetMatchingNode(node, true);
  200.         }
  201.        
  202.         public override XmlNode Item(int index)
  203.         {
  204.             if (rootNode == null || index < 0)
  205.                 return null;
  206.            
  207.             if (this.empty == true)
  208.                 return null;
  209.             if (curInd == index)
  210.                 return curElem;
  211.             int nDiff = index - curInd;
  212.             bool bForward = (nDiff > 0);
  213.             if (nDiff < 0)
  214.                 nDiff = -nDiff;
  215.             XmlNode node;
  216.             if ((node = GetNthMatchingNode(curElem, bForward, nDiff)) != null) {
  217.                 curInd = index;
  218.                 curElem = node;
  219.                 return curElem;
  220.             }
  221.             return null;
  222.         }
  223.        
  224.         public override int Count {
  225.             get {
  226.                 if (this.empty == true)
  227.                     return 0;
  228.                 if (this.matchCount < 0) {
  229.                     int currMatchCount = 0;
  230.                     int currChangeCount = this.changeCount;
  231.                     XmlNode node = rootNode;
  232.                     while ((node = GetMatchingNode(node, true)) != null) {
  233.                         currMatchCount++;
  234.                     }
  235.                     if (currChangeCount != this.changeCount) {
  236.                         return currMatchCount;
  237.                     }
  238.                     this.matchCount = currMatchCount;
  239.                 }
  240.                 return this.matchCount;
  241.             }
  242.         }
  243.        
  244.         public override IEnumerator GetEnumerator()
  245.         {
  246.             if (this.empty == true)
  247.                 return new XmlEmptyElementListEnumerator(this);
  248.             ;
  249.             return new XmlElementListEnumerator(this);
  250.         }
  251.     }
  252.    
  253.     internal class XmlElementListEnumerator : IEnumerator
  254.     {
  255.         XmlElementList list;
  256.         XmlNode curElem;
  257.         int changeCount;
  258.         //save the total number that the dom tree has been changed ( insertion and deletetion ) when this enumerator is created
  259.         public XmlElementListEnumerator(XmlElementList list)
  260.         {
  261.             this.list = list;
  262.             this.curElem = null;
  263.             this.changeCount = list.ChangeCount;
  264.         }
  265.        
  266.         public bool MoveNext()
  267.         {
  268.             if (list.ChangeCount != this.changeCount) {
  269.                 //the number mismatch, there is new change(s) happened since last MoveNext() is called.
  270.                 throw new InvalidOperationException(Res.GetString(Res.Xdom_Enum_ElementList));
  271.             }
  272.             else {
  273.                 curElem = list.GetNextNode(curElem);
  274.             }
  275.             return curElem != null;
  276.         }
  277.        
  278.         public void Reset()
  279.         {
  280.             curElem = null;
  281.             //reset the number of changes to be synced with current dom tree as well
  282.             this.changeCount = list.ChangeCount;
  283.         }
  284.        
  285.         public object Current {
  286.             get { return curElem; }
  287.         }
  288.     }
  289.    
  290.     internal class XmlEmptyElementListEnumerator : IEnumerator
  291.     {
  292.         public XmlEmptyElementListEnumerator(XmlElementList list)
  293.         {
  294.         }
  295.        
  296.         public bool MoveNext()
  297.         {
  298.             return false;
  299.         }
  300.        
  301.         public void Reset()
  302.         {
  303.         }
  304.        
  305.         public object Current {
  306.             get { return null; }
  307.         }
  308.     }
  309.    
  310.     internal class XmlElementListListener
  311.     {
  312.         WeakReference elemList;
  313.         XmlDocument doc;
  314.         XmlNodeChangedEventHandler nodeChangeHandler = null;
  315.        
  316.         internal XmlElementListListener(XmlDocument doc, XmlElementList elemList)
  317.         {
  318.             this.doc = doc;
  319.             this.elemList = new WeakReference(elemList);
  320.             this.nodeChangeHandler = new XmlNodeChangedEventHandler(this.OnListChanged);
  321.             doc.NodeInserted += this.nodeChangeHandler;
  322.             doc.NodeRemoved += this.nodeChangeHandler;
  323.         }
  324.        
  325.         private void OnListChanged(object sender, XmlNodeChangedEventArgs args)
  326.         {
  327.             XmlElementList elemList = (XmlElementList)this.elemList.Target;
  328.             if (null != elemList) {
  329.                 elemList.ConcurrencyCheck(args);
  330.             }
  331.             else {
  332.                 this.doc.NodeInserted -= this.nodeChangeHandler;
  333.                 this.doc.NodeRemoved -= this.nodeChangeHandler;
  334.             }
  335.         }
  336.        
  337.     }
  338. }

Developer Fusion