2 // System.Xml.XmlNodeReaderImpl.cs - implements the core part of XmlNodeReader
5 // Atsushi Enomoto (atsushi@ximian.com)
7 // (C) 2004 Novell Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 // This serves the implementation part of XmlNodeReader except for
38 using System.Collections.Generic;
44 using System.Xml.Schema;
50 internal class XmlNodeReaderImpl : XmlReader, IHasXmlParserContext, IXmlNamespaceResolver
52 internal class XmlNodeReaderImpl : XmlReader, IHasXmlParserContext
58 XmlNode ownerLinkedNode;
59 ReadState state = ReadState.Initial;
66 internal XmlNodeReaderImpl (XmlNodeReaderImpl entityContainer)
67 : this (entityContainer.current)
71 public XmlNodeReaderImpl (XmlNode node)
75 document = startNode.NodeType == XmlNodeType.Document ?
76 startNode as XmlDocument : startNode.OwnerDocument;
78 switch (node.NodeType) {
79 case XmlNodeType.Document:
80 case XmlNodeType.DocumentFragment:
81 case XmlNodeType.EntityReference:
82 ignoreStartNode = true;
92 public override int AttributeCount {
94 if (state != ReadState.Interactive)
97 if (isEndElement || current == null)
99 XmlNode n = ownerLinkedNode;
100 return n.Attributes != null ? n.Attributes.Count : 0;
104 public override string BaseURI {
107 return startNode.BaseURI;
108 return current.BaseURI;
113 public override bool CanReadBinaryContent {
117 public override bool CanReadValueChunk {
121 internal override bool CanReadBinaryContent {
125 internal override bool CanReadValueChunk {
130 public override bool CanResolveEntity {
131 get { return false; }
134 public override int Depth {
136 return current == null ? 0 :
137 current == ownerLinkedNode ? depth :
138 current.NodeType == XmlNodeType.Attribute ? depth + 1 :
143 public override bool EOF {
144 get { return state == ReadState.EndOfFile || state == ReadState.Error; }
147 public override bool HasAttributes {
149 if (isEndElement || current == null)
152 // MS BUG: inconsistent return value between XmlTextReader and XmlNodeReader.
153 // As for attribute and its descendants, XmlReader returns element's HasAttributes.
154 XmlNode n = ownerLinkedNode;
156 if (n.Attributes == null ||
157 n.Attributes.Count == 0)
164 public override bool HasValue {
169 switch (current.NodeType) {
170 case XmlNodeType.Element:
171 case XmlNodeType.EntityReference:
172 case XmlNodeType.Document:
173 case XmlNodeType.DocumentFragment:
174 case XmlNodeType.Notation:
175 case XmlNodeType.EndElement:
176 case XmlNodeType.EndEntity:
185 public override bool IsDefault {
190 if (current.NodeType != XmlNodeType.Attribute)
194 return !((XmlAttribute) current).Specified;
199 public override bool IsEmptyElement {
204 if(current.NodeType == XmlNodeType.Element)
205 return ((XmlElement) current).IsEmpty;
213 public override string this [int i] {
214 get { return GetAttribute (i); }
217 public override string this [string name] {
218 get { return GetAttribute (name); }
221 public override string this [string name, string namespaceURI] {
222 get { return GetAttribute (name, namespaceURI); }
226 public override string LocalName {
231 switch (current.NodeType) {
232 case XmlNodeType.Attribute:
233 case XmlNodeType.DocumentType:
234 case XmlNodeType.Element:
235 case XmlNodeType.EntityReference:
236 case XmlNodeType.ProcessingInstruction:
237 case XmlNodeType.XmlDeclaration:
238 return current.LocalName;
245 public override string Name {
250 switch (current.NodeType) {
251 case XmlNodeType.Attribute:
252 case XmlNodeType.DocumentType:
253 case XmlNodeType.Element:
254 case XmlNodeType.EntityReference:
255 case XmlNodeType.ProcessingInstruction:
256 case XmlNodeType.XmlDeclaration:
264 public override string NamespaceURI {
269 return current.NamespaceURI;
273 public override XmlNameTable NameTable {
274 get { return document.NameTable; }
277 public override XmlNodeType NodeType {
280 return XmlNodeType.None;
282 return isEndElement ? XmlNodeType.EndElement : current.NodeType;
286 public override string Prefix {
291 return current.Prefix;
297 public override char QuoteChar {
304 public override ReadState ReadState {
305 get { return state; }
309 public override IXmlSchemaInfo SchemaInfo {
310 get { return current != null ? current.SchemaInfo : null; }
314 public override string Value {
316 if (NodeType == XmlNodeType.DocumentType)
317 return ((XmlDocumentType) current).InternalSubset;
319 return HasValue ? current.Value : String.Empty;
323 public override string XmlLang {
326 return startNode.XmlLang;
328 return current.XmlLang;
332 public override XmlSpace XmlSpace {
335 return startNode.XmlSpace;
337 return current.XmlSpace;
344 public override void Close ()
347 state = ReadState.Closed;
350 public override string GetAttribute (int attributeIndex)
352 if (NodeType == XmlNodeType.XmlDeclaration) {
353 XmlDeclaration decl = current as XmlDeclaration;
354 if (attributeIndex == 0)
356 else if (attributeIndex == 1) {
357 if (decl.Encoding != String.Empty)
358 return decl.Encoding;
359 else if (decl.Standalone != String.Empty)
360 return decl.Standalone;
362 else if (attributeIndex == 2 &&
363 decl.Encoding != String.Empty && decl.Standalone != null)
364 return decl.Standalone;
365 throw new ArgumentOutOfRangeException ("Index out of range.");
366 } else if (NodeType == XmlNodeType.DocumentType) {
367 XmlDocumentType doctype = current as XmlDocumentType;
368 if (attributeIndex == 0) {
369 if (doctype.PublicId != "")
370 return doctype.PublicId;
371 else if (doctype.SystemId != "")
372 return doctype.SystemId;
373 } else if (attributeIndex == 1)
374 if (doctype.PublicId == "" && doctype.SystemId != "")
375 return doctype.SystemId;
376 throw new ArgumentOutOfRangeException ("Index out of range.");
379 // This is MS.NET bug which returns attributes in spite of EndElement.
380 if (isEndElement || current == null)
383 if (attributeIndex < 0 || attributeIndex > AttributeCount)
384 throw new ArgumentOutOfRangeException ("Index out of range.");
386 return ownerLinkedNode.Attributes [attributeIndex].Value;
389 public override string GetAttribute (string name)
391 // This is MS.NET bug which returns attributes in spite of EndElement.
392 if (isEndElement || current == null)
395 if (NodeType == XmlNodeType.XmlDeclaration)
396 return GetXmlDeclarationAttribute (name);
397 else if (NodeType == XmlNodeType.DocumentType)
398 return GetDocumentTypeAttribute (name);
400 if (ownerLinkedNode.Attributes == null)
402 XmlAttribute attr = ownerLinkedNode.Attributes [name];
409 public override string GetAttribute (string name, string namespaceURI)
411 // This is MS.NET bug which returns attributes in spite of EndElement.
412 if (isEndElement || current == null)
415 if (NodeType == XmlNodeType.XmlDeclaration)
416 return GetXmlDeclarationAttribute (name);
417 else if (NodeType == XmlNodeType.DocumentType)
418 return GetDocumentTypeAttribute (name);
420 if (ownerLinkedNode.Attributes == null)
422 XmlAttribute attr = ownerLinkedNode.Attributes [name, namespaceURI];
424 return null; // In fact MS.NET returns null instead of String.Empty.
429 private string GetXmlDeclarationAttribute (string name)
431 XmlDeclaration decl = current as XmlDeclaration;
436 // This is MS.NET bug that XmlNodeReturns in case of string.empty.
437 return decl.Encoding != String.Empty ? decl.Encoding : null;
439 return decl.Standalone;
444 private string GetDocumentTypeAttribute (string name)
446 XmlDocumentType doctype = current as XmlDocumentType;
449 return doctype.PublicId;
451 return doctype.SystemId;
456 XmlParserContext IHasXmlParserContext.ParserContext {
458 return new XmlParserContext (document.NameTable,
460 new XmlNamespaceManager (document.NameTable) :
461 current.ConstructNamespaceManager (),
462 document.DocumentType != null ? document.DocumentType.DTD : null,
463 current == null ? document.BaseURI : current.BaseURI,
464 XmlLang, XmlSpace, Encoding.Unicode);
469 public IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
471 IDictionary<string, string> table = new Dictionary<string, string> ();
472 XmlNode n = current ?? startNode;
474 if (n.NodeType == XmlNodeType.Document)
476 for (int i = 0; i < n.Attributes.Count; i++) {
477 XmlAttribute a = n.Attributes [i];
478 if (a.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
479 string key = a.Prefix == XmlNamespaceManager.PrefixXmlns ? a.LocalName : String.Empty;
480 if (!table.ContainsKey (key))
481 table.Add (key, a.Value);
484 if (scope == XmlNamespaceScope.Local)
488 if (scope == XmlNamespaceScope.All)
489 table.Add (XmlNamespaceManager.PrefixXml, XmlNamespaceManager.XmlnsXml);
494 private XmlElement GetCurrentElement ()
496 XmlElement el = null;
497 switch (current.NodeType) {
498 case XmlNodeType.Attribute:
499 el = ((XmlAttribute) current).OwnerElement;
501 case XmlNodeType.Element:
502 el = (XmlElement) current;
504 case XmlNodeType.Text:
505 case XmlNodeType.CDATA:
506 case XmlNodeType.EntityReference:
507 case XmlNodeType.Comment:
508 case XmlNodeType.SignificantWhitespace:
509 case XmlNodeType.Whitespace:
510 case XmlNodeType.ProcessingInstruction:
511 el = current.ParentNode as XmlElement;
517 public override string LookupNamespace (string prefix)
522 XmlElement el = GetCurrentElement ();
524 for (; el != null; el = el.ParentNode as XmlElement) {
525 for (int i = 0; i < el.Attributes.Count; i++) {
526 XmlAttribute attr = el.Attributes [i];
527 if (attr.NamespaceURI != XmlNamespaceManager.XmlnsXmlns)
530 if (attr.Prefix == "")
533 else if (attr.LocalName == prefix)
540 case XmlNamespaceManager.PrefixXml:
541 return XmlNamespaceManager.XmlnsXml;
542 case XmlNamespaceManager.PrefixXmlns:
543 return XmlNamespaceManager.XmlnsXmlns;
549 public string LookupPrefix (string ns)
551 return LookupPrefix (ns, false);
554 public string LookupPrefix (string ns, bool atomizedNames)
559 XmlElement el = GetCurrentElement ();
561 for (; el != null; el = el.ParentNode as XmlElement) {
562 for (int i = 0; i < el.Attributes.Count; i++) {
563 XmlAttribute attr = el.Attributes [i];
565 if (!Object.ReferenceEquals (attr.NamespaceURI, XmlNamespaceManager.XmlnsXmlns))
567 if (Object.ReferenceEquals (attr.Value, ns))
568 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
569 return attr.Prefix != String.Empty ? attr.LocalName : String.Empty;
571 if (attr.NamespaceURI != XmlNamespaceManager.XmlnsXmlns)
573 if (attr.Value == ns)
574 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
575 return attr.Prefix != String.Empty ? attr.LocalName : String.Empty;
580 case XmlNamespaceManager.XmlnsXml:
581 return XmlNamespaceManager.PrefixXml;
582 case XmlNamespaceManager.XmlnsXmlns:
583 return XmlNamespaceManager.PrefixXmlns;
589 public override void MoveToAttribute (int attributeIndex)
591 if (isEndElement || attributeIndex < 0 || attributeIndex > AttributeCount)
592 throw new ArgumentOutOfRangeException ();
594 state = ReadState.Interactive;
595 current = ownerLinkedNode.Attributes [attributeIndex];
598 public override bool MoveToAttribute (string name)
600 if (isEndElement || current == null)
602 XmlNode tmpCurrent = current;
603 if (current.ParentNode.NodeType == XmlNodeType.Attribute)
604 current = current.ParentNode;
606 if (ownerLinkedNode.Attributes == null)
608 XmlAttribute attr = ownerLinkedNode.Attributes [name];
610 current = tmpCurrent;
619 public override bool MoveToAttribute (string name, string namespaceURI)
621 if (isEndElement || current == null)
624 if (ownerLinkedNode.Attributes == null)
626 XmlAttribute attr = ownerLinkedNode.Attributes [name, namespaceURI];
635 public override bool MoveToElement ()
639 XmlNode n = ownerLinkedNode;
647 public override bool MoveToFirstAttribute ()
652 if (ownerLinkedNode.Attributes == null)
654 if(ownerLinkedNode.Attributes.Count > 0)
656 current = ownerLinkedNode.Attributes [0];
663 public override bool MoveToNextAttribute ()
668 XmlNode anode = current;
669 if (current.NodeType != XmlNodeType.Attribute) {
670 // then it's either an attribute child or anything on the tree.
671 if (current.ParentNode == null || // document, or non-tree node
672 current.ParentNode.NodeType != XmlNodeType.Attribute) // not an attr value
673 return MoveToFirstAttribute ();
674 anode = current.ParentNode;
678 XmlAttributeCollection ac = ((XmlAttribute) anode).OwnerElement.Attributes;
679 for (int i=0; i<ac.Count-1; i++)
681 XmlAttribute attr = ac [i];
695 public override bool Read ()
697 // FIXME: at some stage inlining might work effectively.
700 case ReadState.EndOfFile:
701 case ReadState.Error:
702 case ReadState.Closed:
711 bool ret = ReadContent ();
712 ownerLinkedNode = current;
718 if (ReadState == ReadState.Initial) {
720 state = ReadState.Interactive;
721 // when startNode is document or fragment
723 current = startNode.FirstChild;
724 if (current == null) {
725 state = ReadState.Error;
733 // don't step into EntityReference's children. Also
734 // avoid re-entering children of already-consumed
735 // element (i.e. when it is regarded as EndElement).
737 !isEndElement && current.NodeType != XmlNodeType.EntityReference ?
738 current.FirstChild : null;
739 if (firstChild != null) {
740 isEndElement = false;
741 current = firstChild;
746 if (current == startNode) { // Currently it is on the start node.
747 if (IsEmptyElement || isEndElement) {
748 // The start node is already consumed.
749 isEndElement = false;
751 state = ReadState.EndOfFile;
754 // The start node is the only element
755 // which should be processed. Now it
756 // is set as EndElement.
761 if (!isEndElement && !IsEmptyElement &&
762 current.NodeType == XmlNodeType.Element) {
763 // element, currently not EndElement, and has
764 // no child. (such as <foo></foo>, which
765 // should become EndElement).
770 // If NextSibling is available, move to there.
771 XmlNode next = current.NextSibling;
773 isEndElement = false;
778 // Otherwise, parent.
779 XmlNode parent = current.ParentNode;
780 if (parent == null || parent == startNode && ignoreStartNode) {
781 // Parent is not available, or reached to
782 // the start node. This reader never sets
783 // startNode as current if it was originally
784 // ignored (e.g. startNode is XmlDocument).
785 isEndElement = false;
787 state = ReadState.EndOfFile;
790 // Parent was available, so return it as
799 public override bool ReadAttributeValue ()
801 if (current.NodeType == XmlNodeType.Attribute) {
802 if (current.FirstChild == null)
804 current = current.FirstChild;
806 } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
807 if (current.NextSibling == null)
809 current = current.NextSibling;
815 public override string ReadString ()
817 return base.ReadString ();
820 public override void ResolveEntity ()
822 throw new NotSupportedException ("Should not happen.");
825 public override void Skip ()
827 // Why is this overriden? Such skipping might raise
828 // (or ignore) unexpected validation error.