2 // System.Xml.XmlNodeReader.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
13 using System.Collections;
19 public class XmlNodeReader : XmlReader
24 ReadState state = ReadState.Initial;
27 bool nextIsEndElement; // used for ReadString()
29 StringBuilder valueBuilder = new StringBuilder ();
30 XmlNamespaceManager defaultNsmgr;
31 Stack entityReaderStack = new Stack ();
32 XmlTextReader entityReader;
34 private XmlNode ownerElement {
36 if (current.ParentNode != null && current.ParentNode.NodeType == XmlNodeType.Attribute)
37 return ((XmlAttribute) current.ParentNode).OwnerElement;
38 return (current.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)current).OwnerElement : current;
44 public XmlNodeReader (XmlNode node)
47 document = startNode.NodeType == XmlNodeType.Document ?
48 startNode as XmlDocument : startNode.OwnerDocument;
50 if (node.NodeType != XmlNodeType.Document
51 && node.NodeType != XmlNodeType.DocumentFragment)
53 defaultNsmgr = new XmlNamespaceManager (this.NameTable);
61 public override int AttributeCount {
63 if (entityReader != null)
64 return entityReader.ReadState == ReadState.Interactive ?
65 entityReader.AttributeCount : 0;
67 if (isEndElement || current == null || current.Attributes == null)
69 return ownerElement.Attributes.Count;
73 public override string BaseURI {
75 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
76 return entityReader.BaseURI;
80 return current.BaseURI;
84 public override bool CanResolveEntity {
90 public override int Depth {
92 if (entityReader != null && entityReader.ReadState == ReadState.Interactive)
93 return entityReader.Depth + depth + entityReaderStack.Count + 1;
99 public override bool EOF {
101 return this.ReadState == ReadState.EndOfFile
102 || this.ReadState == ReadState.Error;
106 public override bool HasAttributes {
108 if (entityReader != null)
109 return entityReader.ReadState == ReadState.Interactive ?
110 entityReader.HasAttributes : false;
112 if (isEndElement || current == null)
115 if (current.Attributes == null ||
116 current.Attributes.Count == 0)
123 public override bool HasValue {
125 if (entityReader != null)
126 return entityReader.ReadState == ReadState.Interactive ?
127 entityReader.IsDefault : false;
132 if (current.NodeType == XmlNodeType.Element ||
133 current.NodeType == XmlNodeType.EntityReference ||
134 current.NodeType == XmlNodeType.Document ||
135 current.NodeType == XmlNodeType.DocumentFragment ||
136 current.NodeType == XmlNodeType.Notation ||
137 current.NodeType == XmlNodeType.EndElement ||
138 current.NodeType == XmlNodeType.EndEntity)
146 public override bool IsDefault {
148 if (entityReader != null)
149 return entityReader.ReadState == ReadState.Interactive ?
150 entityReader.IsDefault : false;
155 if (current.NodeType != XmlNodeType.Attribute)
159 return ((XmlAttribute) current).isDefault;
164 public override bool IsEmptyElement {
166 if (entityReader != null)
167 return entityReader.ReadState == ReadState.Interactive ?
168 entityReader.IsDefault : false;
173 if(current.NodeType == XmlNodeType.Element)
174 return ((XmlElement) current).IsEmpty;
180 public override string this [int i] {
181 get { return GetAttribute (i); }
184 public override string this [string name] {
185 get { return GetAttribute (name); }
188 public override string this [string name, string namespaceURI] {
189 get { return GetAttribute (name, namespaceURI); }
192 public override string LocalName {
194 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
195 return entityReader.LocalName;
200 switch (current.NodeType) {
201 case XmlNodeType.Attribute:
202 case XmlNodeType.DocumentType:
203 case XmlNodeType.Element:
204 case XmlNodeType.EntityReference:
205 case XmlNodeType.ProcessingInstruction:
206 case XmlNodeType.XmlDeclaration:
207 return current.LocalName;
214 public override string Name {
216 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
217 return entityReader.Name;
222 switch (current.NodeType) {
223 case XmlNodeType.Attribute:
224 case XmlNodeType.DocumentType:
225 case XmlNodeType.Element:
226 case XmlNodeType.EntityReference:
227 case XmlNodeType.ProcessingInstruction:
228 case XmlNodeType.XmlDeclaration:
236 public override string NamespaceURI {
238 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
239 return entityReader.NamespaceURI;
244 return current.NamespaceURI;
248 public override XmlNameTable NameTable {
249 get { return document.NameTable; }
252 public override XmlNodeType NodeType {
254 if (entityReader != null)
255 switch (entityReader.ReadState) {
256 case ReadState.Interactive:
257 return entityReader.NodeType;
258 case ReadState.Initial:
259 return XmlNodeType.EntityReference;
260 case ReadState.EndOfFile:
261 return XmlNodeType.EndEntity;
265 return XmlNodeType.None;
267 return isEndElement ? XmlNodeType.EndElement : current.NodeType;
271 public override string Prefix {
273 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
274 return entityReader.Prefix;
279 return current.Prefix;
283 public override char QuoteChar {
285 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
286 return entityReader.QuoteChar;
292 public override ReadState ReadState {
293 get { return state; }
296 public override string Value {
298 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
299 return entityReader.Value;
301 if (NodeType == XmlNodeType.DocumentType)
302 return ((XmlDocumentType) current).InternalSubset;
304 return HasValue ? current.Value : String.Empty;
308 public override string XmlLang {
310 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
311 return entityReader.XmlLang;
316 return current.XmlLang;
320 public override XmlSpace XmlSpace {
322 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
323 return entityReader.XmlSpace;
326 return XmlSpace.None;
328 return current.XmlSpace;
335 // If current entityReference is a child of an attribute,
336 // then MoveToAttribute simply means that we no more need this entity reader.
337 // Otherwise, this invokation means that
338 // it is expected to move to resolved (maybe) element's attribute.
340 // This rule applies to many methods like MoveTo*Attribute().
341 private bool CheckAndResetEntityReaderOnMoveToAttribute ()
343 if (entityReader == null)
346 if (current != null && current.ParentNode != null &&
347 current.ParentNode.NodeType == XmlNodeType.Attribute) {
348 entityReader.Close ();
349 entityReader = entityReaderStack.Count > 0 ?
350 entityReaderStack.Pop () as XmlTextReader : null;
357 public override void Close ()
359 if (entityReader != null)
360 entityReader.Close ();
361 while (entityReaderStack.Count > 0)
362 ((XmlTextReader) entityReaderStack.Pop ()).Close ();
365 state = ReadState.Closed;
368 public override string GetAttribute (int attributeIndex)
370 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
371 return entityReader.GetAttribute (attributeIndex);
373 if (NodeType == XmlNodeType.XmlDeclaration) {
374 XmlDeclaration decl = current as XmlDeclaration;
375 if (attributeIndex == 0)
377 else if (attributeIndex == 1) {
378 if (decl.Encoding != String.Empty)
379 return decl.Encoding;
380 else if (decl.Standalone != String.Empty)
381 return decl.Standalone;
383 else if (attributeIndex == 2 &&
384 decl.Encoding != String.Empty && decl.Standalone != null)
385 return decl.Standalone;
386 throw new ArgumentOutOfRangeException ("Index out of range.");
387 } else if (NodeType == XmlNodeType.DocumentType) {
388 XmlDocumentType doctype = current as XmlDocumentType;
389 if (attributeIndex == 0) {
390 if (doctype.PublicId != "")
391 return doctype.PublicId;
392 else if (doctype.SystemId != "")
393 return doctype.SystemId;
394 } else if (attributeIndex == 1)
395 if (doctype.PublicId == "" && doctype.SystemId != "")
396 return doctype.SystemId;
397 throw new ArgumentOutOfRangeException ("Index out of range.");
400 // This is MS.NET bug which returns attributes in spite of EndElement.
401 if (isEndElement || current == null)
404 if (attributeIndex < 0 || attributeIndex > AttributeCount)
405 throw new ArgumentOutOfRangeException ("Index out of range.");
407 return ownerElement.Attributes [attributeIndex].Value;
410 public override string GetAttribute (string name)
412 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
413 return entityReader.GetAttribute (name);
415 // This is MS.NET bug which returns attributes in spite of EndElement.
416 if (isEndElement || current == null)
419 if (NodeType == XmlNodeType.XmlDeclaration)
420 return GetXmlDeclarationAttribute (name);
421 else if (NodeType == XmlNodeType.DocumentType)
422 return GetDocumentTypeAttribute (name);
424 XmlAttribute attr = ownerElement.Attributes [name];
431 public override string GetAttribute (string name, string namespaceURI)
433 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
434 return entityReader.GetAttribute (name, namespaceURI);
436 // This is MS.NET bug which returns attributes in spite of EndElement.
437 if (isEndElement || current == null)
440 if (NodeType == XmlNodeType.XmlDeclaration)
441 return GetXmlDeclarationAttribute (name);
442 else if (NodeType == XmlNodeType.DocumentType)
443 return GetDocumentTypeAttribute (name);
445 XmlAttribute attr = ownerElement.Attributes [name, namespaceURI];
447 return null; // In fact MS.NET returns null instead of String.Empty.
452 private string GetXmlDeclarationAttribute (string name)
454 XmlDeclaration decl = current as XmlDeclaration;
459 // This is MS.NET bug that XmlNodeReturns in case of string.empty.
460 return decl.Encoding != String.Empty ? decl.Encoding : null;
462 return decl.Standalone;
467 private string GetDocumentTypeAttribute (string name)
469 XmlDocumentType doctype = current as XmlDocumentType;
472 return doctype.PublicId;
474 return doctype.SystemId;
479 internal XmlParserContext GetInternalParserContext ()
481 if (entityReader != null)
482 return entityReader.GetInternalParserContext ();
484 return new XmlParserContext (document.NameTable,
485 current.ConstructNamespaceManager (),
489 public override string LookupNamespace (string prefix)
491 if (entityReader != null && entityReader.ReadState != ReadState.Initial)
492 return entityReader.LookupNamespace (prefix);
497 XmlAttribute curAttr = current as XmlAttribute;
498 XmlNode target = curAttr != null ? curAttr.OwnerElement : current;
502 XmlAttribute attr = target.Attributes ["xmlns"];
505 target = target.ParentNode;
506 } while (target.NodeType != XmlNodeType.Document);
508 string name = "xmlns:" + prefix;
510 XmlAttribute attr = target.Attributes [name];
513 target = target.ParentNode;
514 } while (target.NodeType != XmlNodeType.Document);
516 return defaultNsmgr.LookupNamespace (prefix);
519 public override void MoveToAttribute (int attributeIndex)
521 if (entityReader != null) {
522 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ()) {
523 entityReader.MoveToAttribute (attributeIndex);
526 // And in case of abondoning entityReader, go on...
529 if (isEndElement || attributeIndex < 0 || attributeIndex > AttributeCount)
530 throw new ArgumentOutOfRangeException ();
532 state = ReadState.Interactive;
533 current = ownerElement.Attributes [attributeIndex];
536 public override bool MoveToAttribute (string name)
538 if (entityReader != null) {
539 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ())
540 return entityReader.MoveToAttribute (name);
541 // And in case of abondoning entityReader, go on...
544 if (isEndElement || current == null)
546 XmlNode tmpCurrent = current;
547 if (current.ParentNode.NodeType == XmlNodeType.Attribute)
548 current = current.ParentNode;
550 XmlAttribute attr = ownerElement.Attributes [name];
552 current = tmpCurrent;
561 public override bool MoveToAttribute (string name, string namespaceURI)
563 if (entityReader != null) {
564 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ())
565 return entityReader.MoveToAttribute (name, namespaceURI);
566 // And in case of abondoning entityReader, go on...
569 if (isEndElement || current == null)
572 XmlAttribute attr = ownerElement.Attributes [name, namespaceURI];
581 private void MoveToParentElement ()
583 // This is buggy. It is not only the case when EndElement = true.
586 current = current.ParentNode;
589 public override bool MoveToElement ()
591 if (entityReader != null) {
592 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ())
593 return entityReader.MoveToElement ();
594 // And in case of abondoning entityReader, go on...
599 if (current.NodeType == XmlNodeType.Attribute) {
600 current = ((XmlAttribute) current).OwnerElement;
606 public override bool MoveToFirstAttribute ()
608 if (entityReader != null) {
609 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ())
610 return entityReader.MoveToFirstAttribute ();
611 // And in case of abondoning entityReader, go on...
617 if(ownerElement.Attributes.Count > 0)
619 current = ownerElement.Attributes [0];
626 public override bool MoveToNextAttribute ()
628 if (entityReader != null) {
629 if (!this.CheckAndResetEntityReaderOnMoveToAttribute ())
630 return entityReader.MoveToNextAttribute ();
631 // And in case of abondoning entityReader, go on...
637 if (current.NodeType != XmlNodeType.Attribute)
638 return MoveToFirstAttribute ();
641 XmlAttributeCollection ac = ((XmlAttribute) current).OwnerElement.Attributes;
642 for (int i=0; i<ac.Count-1; i++)
644 XmlAttribute attr = ac [i];
658 private bool MoveToNextSibling ()
660 if (nextIsEndElement) {
661 // nextIsEndElement is set only by ReadString.
662 nextIsEndElement = false;
663 MoveToParentElement ();
664 } else if (alreadyRead) {
666 return current != null;
668 if (current.NextSibling != null) {
669 isEndElement = false;
670 current = current.NextSibling;
672 MoveToParentElement ();
674 if (current == null) {
675 state = ReadState.EndOfFile;
682 public override bool Read ()
687 this.CheckAndResetEntityReaderOnMoveToAttribute ();
688 if (entityReader != null) {
689 // Read finalizes entity reader.
690 switch (entityReader.ReadState) {
691 case ReadState.Interactive:
692 case ReadState.Initial:
693 // If it is ended, then other properties/methods will take care.
694 entityReader.Read ();
697 entityReader = entityReaderStack.Count > 0 ?
698 entityReaderStack.Pop () as XmlTextReader : null;
704 if (ReadState == ReadState.Initial) {
706 state = ReadState.Interactive;
707 // when startNode is document or fragment
709 current = startNode.FirstChild;
712 if (current == null) {
713 state = ReadState.Error;
721 if (IsEmptyElement || isEndElement) {
722 // Then go up and move to next.
723 // If no more nodes, then set EOF.
724 isEndElement = false;
725 if (current.ParentNode == null
726 || current.ParentNode.NodeType == XmlNodeType.Document
727 || current.ParentNode.NodeType == XmlNodeType.DocumentFragment) {
729 state = ReadState.EndOfFile;
731 } else if (current.NextSibling == null) {
733 current = current.ParentNode;
737 current = current.NextSibling;
741 } else if (nextIsEndElement) {
742 // nextIsEndElement is set only by ReadString.
743 nextIsEndElement = false;
745 return current != null;
747 } else if (alreadyRead) {
749 return current != null;
752 if (!isEndElement && current.FirstChild != null) {
753 isEndElement = false;
754 current = current.FirstChild;
756 } else if (current.NodeType == XmlNodeType.Element) {
758 if (current.FirstChild != null)
761 MoveToNextSibling ();
763 return current != null;
766 public override bool ReadAttributeValue ()
768 if (entityReader != null) {
769 switch (entityReader.ReadState) {
770 case ReadState.Interactive:
771 case ReadState.Initial:
772 // If it is ended, then other properties/methods will take care.
773 return entityReader.ReadAttributeValue ();
775 entityReader = entityReaderStack.Count > 0 ?
776 entityReaderStack.Pop () as XmlTextReader : null;
778 return ReadAttributeValue ();
782 if (current.NodeType == XmlNodeType.Attribute) {
783 current = current.FirstChild;
784 return current != null;
785 } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
786 if (current.NextSibling == null)
788 current = current.NextSibling;
795 // Its traversal behavior is almost same as Read().
796 public override string ReadInnerXml ()
798 if (entityReader != null) {
799 if (entityReader.EOF) {
800 entityReader = entityReaderStack.Count > 0 ?
801 entityReaderStack.Pop () as XmlTextReader : null;
802 return ReadInnerXml ();
804 return entityReader.ReadInnerXml ();
807 if (this.state != ReadState.Interactive)
810 XmlNode initial = current;
811 // Almost copied from XmlTextReader.
813 case XmlNodeType.Attribute:
815 case XmlNodeType.Element:
819 int startDepth = depth;
824 if (NodeType ==XmlNodeType.None)
825 throw new XmlException ("unexpected end of xml.");
826 else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
831 return initial.InnerXml;
832 case XmlNodeType.None:
840 // Its traversal behavior is almost same as Read().
841 public override string ReadOuterXml ()
843 if (entityReader != null) {
844 if (entityReader.EOF) {
845 entityReader = entityReaderStack.Count > 0 ?
846 entityReaderStack.Pop () as XmlTextReader : null;
847 return ReadOuterXml ();
849 return entityReader.ReadOuterXml ();
852 if (NodeType == XmlNodeType.EndElement)
854 XmlNode initial = current;
857 case XmlNodeType.Attribute:
858 return current.OuterXml;
859 case XmlNodeType.Element:
860 if (NodeType == XmlNodeType.Element && !IsEmptyElement)
864 return initial.OuterXml;
865 case XmlNodeType.None:
874 public override string ReadString ()
876 return ReadStringInternal ();
879 public override void ResolveEntity ()
881 if (NodeType != XmlNodeType.EntityReference)
882 throw new InvalidOperationException ("The current node is not an Entity Reference");
883 XmlEntity entity = document.DocumentType != null ?
884 document.DocumentType.Entities.GetNamedItem (Name) as XmlEntity : null;
886 // MS.NET seems simply ignoring undeclared entity reference ;-(
887 string replacementText =
888 (entity != null) ? entity.InnerText : String.Empty;
890 XmlNodeType xmlReaderNodeType =
891 (current.ParentNode != null && current.ParentNode.NodeType == XmlNodeType.Attribute) ?
892 XmlNodeType.Attribute : XmlNodeType.Element;
894 XmlParserContext ctx = null;
895 if (entityReader != null) {
896 entityReaderStack.Push (entityReader);
897 ctx = entityReader.GetInternalParserContext ();
900 ctx = new XmlParserContext (document.NameTable,
901 current.ConstructNamespaceManager (),
902 document.DocumentType != null ? document.DocumentType.DTD : null,
903 BaseURI, XmlLang, XmlSpace, Encoding.Unicode);
905 entityReader = new XmlTextReader (replacementText, xmlReaderNodeType, ctx);
906 entityReader.MaybeTextDecl = true;
909 public override void Skip ()
911 // Why is this overriden? Such skipping might raise
912 // (or ignore) unexpected validation error.