2005-12-14 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
1 //
2 // System.Xml.XmlDocumentNavigator
3 //
4 // Authors:
5 //   Jason Diamond <jason@injektilo.org>
6 //   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
7 //
8 // (C) 2002 Jason Diamond
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Xml;
36 using System.Xml.Schema;
37 using System.Xml.XPath;
38
39 namespace System.Xml
40 {
41         internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
42         {
43                 #region Constructors
44
45                 internal XmlDocumentNavigator (XmlNode node)
46                         : this (node, null)
47                 {
48                         nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
49                         nsNodeXml.Value = XmlnsXML;
50                         if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
51                                 nsNode = (XmlAttribute) node; 
52                                 node = nsNode.OwnerElement;
53                         }
54                 }
55
56                 private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
57                 {
58                         this.node = node;
59                         this.document = node.NodeType == XmlNodeType.Document ?
60                                 node as XmlDocument : node.OwnerDocument;
61                         this.nsNodeXml = nsNodeXml;
62                 }
63
64                 #endregion
65
66                 #region Fields
67                 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
68                 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
69
70                 private XmlAttribute nsNodeXml;
71                 private XmlNode node;
72                 private XmlDocument document;
73                 // Current namespace node (ancestor's attribute of current node).
74                 private XmlAttribute nsNode;
75                 private ArrayList iteratedNsNames;
76                 #endregion
77
78                 #region Properties
79
80                 public override string BaseURI {
81                         get {
82                                 return node.BaseURI;
83                         }
84                 }
85
86                 public override bool HasAttributes {
87                         get {
88                                 if (NsNode != null)
89                                         return false;
90
91                                 if (node.Attributes != null)
92                                         for (int i = 0; i < node.Attributes.Count; i++)
93                                                 if (node.Attributes [i].NamespaceURI != Xmlns)
94                                                         return true;
95                                 return false;
96                         }
97                 }
98
99                 public override bool HasChildren {
100                         get {
101                                 if (NsNode != null)
102                                         return false;
103
104                                 XPathNodeType nodeType = NodeType;
105                                 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
106                                 return canHaveChildren && GetFirstChild (node) != null;
107                         }
108                 }
109
110                 public override bool IsEmptyElement {
111                         get {
112                                 if (NsNode != null)
113                                         return false;
114
115                                 return node.NodeType == XmlNodeType.Element 
116                                         && ((XmlElement) node).IsEmpty;
117                         }
118                 }
119
120                 public XmlAttribute NsNode {
121                         get { return nsNode; }
122                         set {
123                                 if (value == null)
124                                         iteratedNsNames = null;
125                                 else
126                                 {
127                                         if (iteratedNsNames == null)
128                                                 iteratedNsNames = new ArrayList();
129                                         else
130                                         {
131                                                 if (iteratedNsNames.IsReadOnly)
132                                                         iteratedNsNames = new ArrayList(iteratedNsNames);
133                                         }
134                                         iteratedNsNames.Add (value.Name);
135                                 }
136                                 nsNode = value;
137                         }
138                 }
139
140                 public override string LocalName {
141                         get {
142                                 XmlAttribute nsNode = NsNode;
143                                 if (nsNode != null) {
144                                         if (nsNode == nsNodeXml)
145                                                 return "xml";
146                                         else
147                                                 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
148                                 }
149
150                                 XPathNodeType nodeType = NodeType;
151                                 bool canHaveName = 
152                                         nodeType == XPathNodeType.Element || 
153                                         nodeType == XPathNodeType.Attribute || 
154                                         nodeType == XPathNodeType.ProcessingInstruction ||
155                                         nodeType == XPathNodeType.Namespace;
156                                 return canHaveName ? node.LocalName : String.Empty;
157                         }
158                 }
159
160                 public override string Name {
161                         get {
162                                 if (NsNode != null)
163                                         return LocalName;
164
165                                 XPathNodeType nodeType = NodeType;
166                                 bool canHaveName = 
167                                         nodeType == XPathNodeType.Element || 
168                                         nodeType == XPathNodeType.Attribute || 
169                                         nodeType == XPathNodeType.ProcessingInstruction ||
170                                         nodeType == XPathNodeType.Namespace;
171                                 return canHaveName ? node.Name : String.Empty;
172                         }
173                 }
174
175                 public override string NamespaceURI {
176                         get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
177                 }
178
179                 public override XmlNameTable NameTable {
180                         get {
181                                 return document.NameTable;
182                         }
183                 }
184
185                 public override XPathNodeType NodeType {
186                         get {
187                                 if (NsNode != null)
188                                         return XPathNodeType.Namespace;
189                                 XmlNode n = node;
190                                 bool sw = false;
191                                 do {
192                                         switch (n.NodeType) {
193                                         case XmlNodeType.SignificantWhitespace:
194                                                 sw = true;
195                                                 n = GetNextSibling (n);
196                                                 break;
197                                         case XmlNodeType.Whitespace:
198                                                 n = GetNextSibling (n);
199                                                 break;
200                                         case XmlNodeType.Text:
201                                         case XmlNodeType.CDATA:
202                                                 return XPathNodeType.Text;
203                                         default:
204                                                 n = null;
205                                                 break;
206                                         }
207                                 } while (n != null);
208                                 return sw ?
209                                         XPathNodeType.SignificantWhitespace :
210                                         node.XPathNodeType;
211                         }
212                 }
213
214                 public override string Prefix {
215                         get { return (NsNode != null) ? String.Empty : node.Prefix; }
216                 }
217
218 #if NET_2_0
219                 public override IXmlSchemaInfo SchemaInfo {
220                         get { return NsNode != null ? null : node.SchemaInfo; }
221                 }
222
223                 public override object UnderlyingObject {
224                         get { return node; }
225                 }
226 #endif
227
228                 public override string Value {
229                         get {
230                                 switch (NodeType) {
231                                 case XPathNodeType.Attribute:
232                                 case XPathNodeType.Comment:
233                                 case XPathNodeType.ProcessingInstruction:
234                                         return node.Value;
235                                 case XPathNodeType.Text:
236                                 case XPathNodeType.Whitespace:
237                                 case XPathNodeType.SignificantWhitespace:
238                                         string value = node.Value;
239                                         for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
240                                                 switch (n.XPathNodeType) {
241                                                 case XPathNodeType.Text:
242                                                 case XPathNodeType.Whitespace:
243                                                 case XPathNodeType.SignificantWhitespace:
244                                                         value += n.Value;
245                                                         continue;
246                                                 }
247                                                 break;
248                                         }
249                                         return value;
250                                 case XPathNodeType.Element:
251                                 case XPathNodeType.Root:
252                                         return node.InnerText;
253                                 case XPathNodeType.Namespace:
254                                         return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
255                                 }
256                                 return String.Empty;
257                         }
258                 }
259
260                 public override string XmlLang {
261                         get {
262                                 return node.XmlLang;
263                         }
264                 }
265
266                 #endregion
267
268                 #region Methods
269
270                 private bool CheckNsNameAppearance (string name, string ns)
271                 {
272                         if (iteratedNsNames != null && iteratedNsNames.Contains (name))
273                                 return true;
274                         // default namespace erasure - just add name and never return this node
275                         if (ns == String.Empty) {
276                                 if (iteratedNsNames == null)
277                                         iteratedNsNames = new ArrayList();
278                                 else
279                                 {
280                                         if (iteratedNsNames.IsReadOnly)
281                                                 iteratedNsNames = new ArrayList(iteratedNsNames);
282                                 }
283                                 iteratedNsNames.Add ("xmlns");
284                                 return true;
285                         }
286
287                         return false;
288                 }
289
290                 public override XPathNavigator Clone ()
291                 {
292                         XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
293                         clone.nsNode = nsNode;
294                         clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
295                         return clone;
296                 }
297
298                 public override string GetAttribute (string localName, string namespaceURI)
299                 {
300                         if (HasAttributes) {
301                                 XmlElement el = Node as XmlElement;
302                                 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
303                         }
304                         return String.Empty;
305                 }
306
307                 public override string GetNamespace (string name)
308                 {
309                         // MSDN says "String.Empty if a matching namespace 
310                         // node is not found or if the navigator is not 
311                         // positioned on an element node", but in fact it
312                         // returns actual namespace for the other nodes.
313                         return Node.GetNamespaceOfPrefix (name);
314                 }
315
316                 public override bool IsDescendant (XPathNavigator other)
317                 {
318                         if (NsNode != null)
319                                 return false;
320                         XmlDocumentNavigator o = other as XmlDocumentNavigator;
321                         if (o == null)
322                                 return false;
323                         XmlNode n =
324                                 o.node.NodeType == XmlNodeType.Attribute ?
325                                 ((XmlAttribute) o.node).OwnerElement :
326                                 o.node.ParentNode;
327                         for (;n != null; n = n.ParentNode)
328                                 if (n == node)
329                                         return true;
330                         return false;
331                 }
332
333                 public override bool IsSamePosition (XPathNavigator other)
334                 {
335                         XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
336                         if (otherDocumentNavigator != null)
337                                 return node == otherDocumentNavigator.node
338                                         && NsNode == otherDocumentNavigator.NsNode;
339                         return false;
340                 }
341
342                 public override bool MoveTo (XPathNavigator other)
343                 {
344                         XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
345                         if (otherDocumentNavigator != null) {
346                                 if (document == otherDocumentNavigator.document) {
347                                         node = otherDocumentNavigator.node;
348                                         NsNode = otherDocumentNavigator.NsNode;
349                                         return true;
350                                 }
351                         }
352                         return false;
353                 }
354
355                 public override bool MoveToAttribute (string localName, string namespaceURI)
356                 {
357                         if (node.Attributes != null) {
358                                 for (int i = 0; i < node.Attributes.Count; i++) {
359                                         XmlAttribute attr = node.Attributes [i];
360                                         if (attr.LocalName == localName
361                                                 && attr.NamespaceURI == namespaceURI) {
362                                                 node = attr;
363                                                 NsNode = null;
364                                                 return true;
365                                         }
366                                 }
367                         }
368                         return false;
369                 }
370
371 #if NET_2_0
372 #else
373                 public override bool MoveToFirst ()
374                 {
375                         return MoveToFirstImpl ();
376                 }
377 #endif
378
379                 public override bool MoveToFirstAttribute ()
380                 {
381                         if (node.Attributes == null)
382                                 return false;
383                         if (NodeType == XPathNodeType.Element) {
384                                 for (int i = 0; i < node.Attributes.Count; i++) {
385                                         XmlAttribute attr = node.Attributes [i];
386                                         if (attr.NamespaceURI != Xmlns) {
387                                                 node = attr;
388                                                 NsNode = null;
389                                                 return true;
390                                         }
391                                 }
392                         }
393                         return false;
394                 }
395
396                 public override bool MoveToFirstChild ()
397                 {
398                         if (HasChildren) {
399                                 XmlNode n = GetFirstChild (node);
400                                 if (n == null)
401                                         return false;
402                                 node = n;
403                                 return true;
404                         }
405                         return false;
406                 }
407
408                 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
409                 {
410                         if (NodeType != XPathNodeType.Element)
411                                 return false;
412                         XmlElement el = node as XmlElement;
413                         if (node.Attributes != null) {
414                                 do {
415                                         for (int i = 0; i < el.Attributes.Count; i++) {
416                                                 XmlAttribute attr = el.Attributes [i];
417                                                 if (attr.NamespaceURI == Xmlns) {
418                                                         if (CheckNsNameAppearance (attr.Name, attr.Value))
419                                                                 continue;
420                                                         NsNode = attr;
421                                                         return true;
422                                                 }
423                                         }
424                                         if (namespaceScope == XPathNamespaceScope.Local)
425                                                 return false;
426                                         el = GetParentNode (el) as XmlElement;
427                                 } while (el != null);
428                         }
429
430                         if (namespaceScope == XPathNamespaceScope.All) {
431                                 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
432                                         return false;
433                                 NsNode = nsNodeXml;
434                                 return true;
435                         }
436                         else
437                                 return false;
438                 }
439
440                 public override bool MoveToId (string id)
441                 {
442                         XmlElement eltNew = document.GetElementById (id);
443                         if (eltNew == null)
444                                 return false;
445
446                         node = eltNew;
447                         return true;
448                 }
449
450                 public override bool MoveToNamespace (string name)
451                 {
452                         if (name == "xml") {
453                                 NsNode = nsNodeXml;
454                                 return true;
455                         }
456
457                         if (NodeType != XPathNodeType.Element)
458                                 return false;
459
460                         XmlElement el = node as XmlElement;
461                         if (node.Attributes != null) {
462                                 do {
463                                         for (int i = 0; i < el.Attributes.Count; i++) {
464                                                 XmlAttribute attr = el.Attributes [i];
465                                                 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
466                                                         NsNode = attr;
467                                                         return true;
468                                                 }
469                                         }
470                                         el = GetParentNode (node) as XmlElement;
471                                 } while (el != null);
472                         }
473                         return false;
474                 }
475
476                 public override bool MoveToNext ()
477                 {
478                         if (NsNode != null)
479                                 return false;
480
481                         XmlNode n = node;
482                         if (NodeType == XPathNodeType.Text) {
483                                 do {
484                                         n = GetNextSibling (n);
485                                         if (n == null)
486                                                 return false;
487                                         switch (n.NodeType) {
488                                         case XmlNodeType.CDATA:
489                                         case XmlNodeType.SignificantWhitespace:
490                                         case XmlNodeType.Text:
491                                         case XmlNodeType.Whitespace:
492                                                 continue;
493                                         default:
494                                                 break;
495                                         }
496                                         break;
497                                 } while (true);
498                         }
499                         else
500                                 n = GetNextSibling (n);
501                         if (n == null)
502                                 return false;
503                         node = n;
504                         return true;
505                 }
506
507                 public override bool MoveToNextAttribute ()
508                 {
509                         if (node == null)
510                                 return false;
511                         if (NodeType != XPathNodeType.Attribute)
512                                 return false;
513
514                         // Find current attribute.
515                         int pos = 0;
516                         XmlElement owner = ((XmlAttribute) node).OwnerElement;
517                         if (owner == null)
518                                 return false;
519
520                         int count = owner.Attributes.Count;
521                         for(; pos < count; pos++)
522                                 if (owner.Attributes [pos] == node)
523                                         break;
524                         if (pos == count)
525                                 return false;   // Where is current attribute? Maybe removed.
526
527                         // Find next attribute.
528                         for(pos++; pos < count; pos++) {
529                                 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
530                                         node = owner.Attributes [pos];
531                                         NsNode = null;
532                                         return true;
533                                 }
534                         }
535                         return false;
536                 }
537
538                 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
539                 {
540                         if (NsNode == nsNodeXml)
541                                 // Current namespace is "xml", so there should be no more namespace nodes.
542                                 return false;
543
544                         if (NsNode == null)
545                                 return false;
546
547                         // Get current attribute's position.
548                         int pos = 0;
549                         XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
550                         if (owner == null)
551                                 return false;
552
553                         int count = owner.Attributes.Count;
554                         for(; pos < count; pos++)
555                                 if (owner.Attributes [pos] == NsNode)
556                                         break;
557                         if (pos == count)
558                                 return false;   // Where is current attribute? Maybe removed.
559
560                         // Find next namespace from the same element as current ns node.
561                         for(pos++; pos < count; pos++) {
562                                 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
563                                         XmlAttribute a = owner.Attributes [pos];
564                                         if (CheckNsNameAppearance (a.Name, a.Value))
565                                                 continue;
566                                         NsNode = a;
567                                         return true;
568                                 }
569                         }
570
571                         // If not found more, then find from ancestors.
572                         // But if scope is Local, then it returns false here.
573                         if (namespaceScope == XPathNamespaceScope.Local)
574                                 return false;
575                         owner = GetParentNode (owner) as XmlElement;
576                         while (owner != null) {
577                                 for (int i = 0; i < owner.Attributes.Count; i++) {
578                                         XmlAttribute attr = owner.Attributes [i];
579                                         if (attr.NamespaceURI == Xmlns) {
580                                                 if (CheckNsNameAppearance (attr.Name, attr.Value))
581                                                         continue;
582                                                 NsNode = attr;
583                                                 return true;
584                                         }
585                                 }
586                                 owner = GetParentNode (owner) as XmlElement;
587                         }
588
589                         if (namespaceScope == XPathNamespaceScope.All) {
590                                 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
591                                         return false;
592                                 NsNode = nsNodeXml;
593                                 return true;
594                         }
595                         return false;
596                 }
597
598                 public override bool MoveToParent ()
599                 {
600                         if (NsNode != null) {
601                                 NsNode = null;
602                                 return true;
603                         }
604                         else if (node.NodeType == XmlNodeType.Attribute) {
605                                 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
606                                 if (ownerElement != null) {
607                                         node = ownerElement;
608                                         NsNode = null;
609                                         return true;
610                                 }
611                                 else
612                                         return false;
613                         }
614                         XmlNode n = GetParentNode (node);
615                         if (n == null)
616                                 return false;
617                         node = n;
618                         NsNode = null;
619                         return true;
620                 }
621
622                 public override bool MoveToPrevious ()
623                 {
624                         if (NsNode != null)
625                                 return false;
626
627                         XmlNode p = GetPreviousSibling (node);
628                         if (p == null)
629                                 return false;
630                         node = p;
631                         return true;
632                 }
633
634                 public override void MoveToRoot ()
635                 {
636                         XmlAttribute attr = node as XmlAttribute;
637                         XmlNode tmp = attr != null ? attr.OwnerElement : node;
638                         for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
639                                 tmp = tmp2;
640                         node = tmp;
641                         NsNode = null;
642                 }
643
644                 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
645
646                 XmlNode IHasXmlNode.GetNode ()
647                 {
648                         return Node;
649                 }
650
651                 private XmlNode GetFirstChild (XmlNode n)
652                 {
653                         if (n.FirstChild == null)
654                                 return null;
655                         switch (n.FirstChild.NodeType) {
656                         case XmlNodeType.XmlDeclaration:
657                         case XmlNodeType.DocumentType:
658                                 return GetNextSibling (n.FirstChild);
659                         case XmlNodeType.EntityReference:
660                                 foreach (XmlNode c in n.ChildNodes) {
661                                         if (c.NodeType == XmlNodeType.EntityReference) {
662                                                 XmlNode ec = GetFirstChild (c);
663                                                 if (ec != null)
664                                                         return ec;
665                                         }
666                                         return c;
667                                 }
668                                 return null;
669                         default:
670                                 return n.FirstChild;
671                         }
672                 }
673
674                 private XmlNode GetLastChild (XmlNode n)
675                 {
676                         if (n.LastChild == null)
677                                 return null;
678                         switch (n.LastChild.NodeType) {
679                         case XmlNodeType.XmlDeclaration:
680                         case XmlNodeType.DocumentType:
681                                 return GetPreviousSibling (n.LastChild);
682                         case XmlNodeType.EntityReference:
683                                 for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
684                                         if (c.NodeType == XmlNodeType.EntityReference) {
685                                                 XmlNode ec = GetLastChild (c);
686                                                 if (ec != null)
687                                                         return ec;
688                                         }
689                                         return c;
690                                 }
691                                 return null;
692                         default:
693                                 return n.LastChild;
694                         }
695                 }
696
697                 private XmlNode GetPreviousSibling (XmlNode n)
698                 {
699                         if (n.PreviousSibling != null) {
700                                 switch (n.PreviousSibling.NodeType) {
701                                 case XmlNodeType.EntityReference:
702                                         XmlNode c = GetLastChild (n.PreviousSibling);
703                                         if (c != null)
704                                                 return c;
705                                         else // empty entity reference etc.
706                                                 return GetPreviousSibling (n.PreviousSibling);
707                                 case XmlNodeType.XmlDeclaration:
708                                 case XmlNodeType.DocumentType:
709                                         return GetPreviousSibling (n.PreviousSibling);
710                                 default:
711                                         return n.PreviousSibling;
712                                 }
713                         } else {
714                                 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
715                                         return null;
716                                 return GetPreviousSibling (n.ParentNode);
717                         }
718                 }
719
720                 private XmlNode GetNextSibling (XmlNode n)
721                 {
722                         if (n.NextSibling != null) {
723                                 switch (n.NextSibling.NodeType) {
724                                 case XmlNodeType.EntityReference:
725                                         XmlNode c = GetFirstChild (n.NextSibling);
726                                         if (c != null)
727                                                 return c;
728                                         else // empty entity reference etc.
729                                                 return GetNextSibling (n.NextSibling);
730                                 case XmlNodeType.XmlDeclaration:
731                                 case XmlNodeType.DocumentType:
732                                         return GetNextSibling (n.NextSibling);
733                                 default:
734                                         return n.NextSibling;
735                                 }
736                         } else {
737                                 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
738                                         return null;
739                                 return GetNextSibling (n.ParentNode);
740                         }
741                 }
742
743                 private XmlNode GetParentNode (XmlNode n)
744                 {
745                         if (n.ParentNode == null)
746                                 return null;
747                         for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
748                                 if (p.NodeType != XmlNodeType.EntityReference)
749                                         return p;
750                         return null;
751                 }
752
753                 #endregion
754         }
755 }