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 #if !NET_2_1 || MONOTOUCH
165 public override bool HasValue
{
170 switch (current
.NodeType
) {
171 case XmlNodeType
.Element
:
172 case XmlNodeType
.EntityReference
:
173 case XmlNodeType
.Document
:
174 case XmlNodeType
.DocumentFragment
:
175 case XmlNodeType
.Notation
:
176 case XmlNodeType
.EndElement
:
177 case XmlNodeType
.EndEntity
:
187 public override bool IsDefault
{
192 if (current
.NodeType
!= XmlNodeType
.Attribute
)
196 return !((XmlAttribute
) current
).Specified
;
201 public override bool IsEmptyElement
{
206 if(current
.NodeType
== XmlNodeType
.Element
)
207 return ((XmlElement
) current
).IsEmpty
;
215 public override string this [int i
] {
216 get { return GetAttribute (i); }
219 public override string this [string name
] {
220 get { return GetAttribute (name); }
223 public override string this [string name
, string namespaceURI
] {
224 get { return GetAttribute (name, namespaceURI); }
228 public override string LocalName
{
233 switch (current
.NodeType
) {
234 case XmlNodeType
.Attribute
:
235 case XmlNodeType
.DocumentType
:
236 case XmlNodeType
.Element
:
237 case XmlNodeType
.EntityReference
:
238 case XmlNodeType
.ProcessingInstruction
:
239 case XmlNodeType
.XmlDeclaration
:
240 return current
.LocalName
;
247 public override string Name
{
252 switch (current
.NodeType
) {
253 case XmlNodeType
.Attribute
:
254 case XmlNodeType
.DocumentType
:
255 case XmlNodeType
.Element
:
256 case XmlNodeType
.EntityReference
:
257 case XmlNodeType
.ProcessingInstruction
:
258 case XmlNodeType
.XmlDeclaration
:
266 public override string NamespaceURI
{
271 return current
.NamespaceURI
;
275 public override XmlNameTable NameTable
{
276 get { return document.NameTable; }
279 public override XmlNodeType NodeType
{
282 return XmlNodeType
.None
;
284 return isEndElement
? XmlNodeType
.EndElement
: current
.NodeType
;
288 public override string Prefix
{
293 return current
.Prefix
;
299 public override char QuoteChar
{
306 public override ReadState ReadState
{
307 get { return state; }
311 public override IXmlSchemaInfo SchemaInfo
{
312 get { return current != null ? current.SchemaInfo : null; }
316 public override string Value
{
318 if (NodeType
== XmlNodeType
.DocumentType
)
319 return ((XmlDocumentType
) current
).InternalSubset
;
321 return HasValue
? current
.Value
: String
.Empty
;
325 public override string XmlLang
{
328 return startNode
.XmlLang
;
330 return current
.XmlLang
;
334 public override XmlSpace XmlSpace
{
337 return startNode
.XmlSpace
;
339 return current
.XmlSpace
;
346 public override void Close ()
349 state
= ReadState
.Closed
;
352 public override string GetAttribute (int attributeIndex
)
354 if (NodeType
== XmlNodeType
.XmlDeclaration
) {
355 XmlDeclaration decl
= current
as XmlDeclaration
;
356 if (attributeIndex
== 0)
358 else if (attributeIndex
== 1) {
359 if (decl
.Encoding
!= String
.Empty
)
360 return decl
.Encoding
;
361 else if (decl
.Standalone
!= String
.Empty
)
362 return decl
.Standalone
;
364 else if (attributeIndex
== 2 &&
365 decl
.Encoding
!= String
.Empty
&& decl
.Standalone
!= null)
366 return decl
.Standalone
;
367 throw new ArgumentOutOfRangeException ("Index out of range.");
368 } else if (NodeType
== XmlNodeType
.DocumentType
) {
369 XmlDocumentType doctype
= current
as XmlDocumentType
;
370 if (attributeIndex
== 0) {
371 if (doctype
.PublicId
!= "")
372 return doctype
.PublicId
;
373 else if (doctype
.SystemId
!= "")
374 return doctype
.SystemId
;
375 } else if (attributeIndex
== 1)
376 if (doctype
.PublicId
== "" && doctype
.SystemId
!= "")
377 return doctype
.SystemId
;
378 throw new ArgumentOutOfRangeException ("Index out of range.");
381 // This is MS.NET bug which returns attributes in spite of EndElement.
382 if (isEndElement
|| current
== null)
385 if (attributeIndex
< 0 || attributeIndex
> AttributeCount
)
386 throw new ArgumentOutOfRangeException ("Index out of range.");
388 return ownerLinkedNode
.Attributes
[attributeIndex
].Value
;
391 public override string GetAttribute (string name
)
393 // This is MS.NET bug which returns attributes in spite of EndElement.
394 if (isEndElement
|| current
== null)
397 if (NodeType
== XmlNodeType
.XmlDeclaration
)
398 return GetXmlDeclarationAttribute (name
);
399 else if (NodeType
== XmlNodeType
.DocumentType
)
400 return GetDocumentTypeAttribute (name
);
402 if (ownerLinkedNode
.Attributes
== null)
404 XmlAttribute attr
= ownerLinkedNode
.Attributes
[name
];
411 public override string GetAttribute (string name
, string namespaceURI
)
413 // This is MS.NET bug which returns attributes in spite of EndElement.
414 if (isEndElement
|| current
== null)
417 if (NodeType
== XmlNodeType
.XmlDeclaration
)
418 return GetXmlDeclarationAttribute (name
);
419 else if (NodeType
== XmlNodeType
.DocumentType
)
420 return GetDocumentTypeAttribute (name
);
422 if (ownerLinkedNode
.Attributes
== null)
424 XmlAttribute attr
= ownerLinkedNode
.Attributes
[name
, namespaceURI
];
426 return null; // In fact MS.NET returns null instead of String.Empty.
431 private string GetXmlDeclarationAttribute (string name
)
433 XmlDeclaration decl
= current
as XmlDeclaration
;
438 // This is MS.NET bug that XmlNodeReturns in case of string.empty.
439 return decl
.Encoding
!= String
.Empty
? decl
.Encoding
: null;
441 return decl
.Standalone
;
446 private string GetDocumentTypeAttribute (string name
)
448 XmlDocumentType doctype
= current
as XmlDocumentType
;
451 return doctype
.PublicId
;
453 return doctype
.SystemId
;
458 XmlParserContext IHasXmlParserContext
.ParserContext
{
460 return new XmlParserContext (document
.NameTable
,
462 new XmlNamespaceManager (document
.NameTable
) :
463 current
.ConstructNamespaceManager (),
464 document
.DocumentType
!= null ? document
.DocumentType
.DTD
: null,
465 current
== null ? document
.BaseURI
: current
.BaseURI
,
466 XmlLang
, XmlSpace
, Encoding
.Unicode
);
471 public IDictionary
<string, string> GetNamespacesInScope (XmlNamespaceScope scope
)
473 IDictionary
<string, string> table
= new Dictionary
<string, string> ();
476 if (n
.NodeType
== XmlNodeType
.Document
)
478 for (int i
= 0; i
< current
.Attributes
.Count
; i
++) {
479 XmlAttribute a
= current
.Attributes
[i
];
480 if (a
.NamespaceURI
== XmlNamespaceManager
.XmlnsXmlns
)
481 table
.Add (a
.Prefix
== XmlNamespaceManager
.PrefixXmlns
? a
.LocalName
: String
.Empty
, a
.Value
);
483 if (scope
== XmlNamespaceScope
.Local
)
487 if (scope
== XmlNamespaceScope
.All
)
488 table
.Add (XmlNamespaceManager
.PrefixXml
, XmlNamespaceManager
.XmlnsXml
);
493 private XmlElement
GetCurrentElement ()
495 XmlElement el
= null;
496 switch (current
.NodeType
) {
497 case XmlNodeType
.Attribute
:
498 el
= ((XmlAttribute
) current
).OwnerElement
;
500 case XmlNodeType
.Element
:
501 el
= (XmlElement
) current
;
503 case XmlNodeType
.Text
:
504 case XmlNodeType
.CDATA
:
505 case XmlNodeType
.EntityReference
:
506 case XmlNodeType
.Comment
:
507 case XmlNodeType
.SignificantWhitespace
:
508 case XmlNodeType
.Whitespace
:
509 case XmlNodeType
.ProcessingInstruction
:
510 el
= current
.ParentNode
as XmlElement
;
516 public override string LookupNamespace (string prefix
)
521 XmlElement el
= GetCurrentElement ();
523 for (; el
!= null; el
= el
.ParentNode
as XmlElement
) {
524 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
525 XmlAttribute attr
= el
.Attributes
[i
];
526 if (attr
.NamespaceURI
!= XmlNamespaceManager
.XmlnsXmlns
)
529 if (attr
.Prefix
== "")
532 else if (attr
.LocalName
== prefix
)
539 case XmlNamespaceManager
.PrefixXml
:
540 return XmlNamespaceManager
.XmlnsXml
;
541 case XmlNamespaceManager
.PrefixXmlns
:
542 return XmlNamespaceManager
.XmlnsXmlns
;
548 public string LookupPrefix (string ns
)
550 return LookupPrefix (ns
, false);
553 public string LookupPrefix (string ns
, bool atomizedNames
)
558 XmlElement el
= GetCurrentElement ();
560 for (; el
!= null; el
= el
.ParentNode
as XmlElement
) {
561 for (int i
= 0; i
< el
.Attributes
.Count
; i
++) {
562 XmlAttribute attr
= el
.Attributes
[i
];
564 if (!Object
.ReferenceEquals (attr
.NamespaceURI
, XmlNamespaceManager
.XmlnsXmlns
))
566 if (Object
.ReferenceEquals (attr
.Value
, ns
))
567 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
568 return attr
.Prefix
!= String
.Empty
? attr
.LocalName
: String
.Empty
;
570 if (attr
.NamespaceURI
!= XmlNamespaceManager
.XmlnsXmlns
)
572 if (attr
.Value
== ns
)
573 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
574 return attr
.Prefix
!= String
.Empty
? attr
.LocalName
: String
.Empty
;
579 case XmlNamespaceManager
.XmlnsXml
:
580 return XmlNamespaceManager
.PrefixXml
;
581 case XmlNamespaceManager
.XmlnsXmlns
:
582 return XmlNamespaceManager
.PrefixXmlns
;
588 public override void MoveToAttribute (int attributeIndex
)
590 if (isEndElement
|| attributeIndex
< 0 || attributeIndex
> AttributeCount
)
591 throw new ArgumentOutOfRangeException ();
593 state
= ReadState
.Interactive
;
594 current
= ownerLinkedNode
.Attributes
[attributeIndex
];
597 public override bool MoveToAttribute (string name
)
599 if (isEndElement
|| current
== null)
601 XmlNode tmpCurrent
= current
;
602 if (current
.ParentNode
.NodeType
== XmlNodeType
.Attribute
)
603 current
= current
.ParentNode
;
605 if (ownerLinkedNode
.Attributes
== null)
607 XmlAttribute attr
= ownerLinkedNode
.Attributes
[name
];
609 current
= tmpCurrent
;
618 public override bool MoveToAttribute (string name
, string namespaceURI
)
620 if (isEndElement
|| current
== null)
623 if (ownerLinkedNode
.Attributes
== null)
625 XmlAttribute attr
= ownerLinkedNode
.Attributes
[name
, namespaceURI
];
634 public override bool MoveToElement ()
638 XmlNode n
= ownerLinkedNode
;
646 public override bool MoveToFirstAttribute ()
651 if (ownerLinkedNode
.Attributes
== null)
653 if(ownerLinkedNode
.Attributes
.Count
> 0)
655 current
= ownerLinkedNode
.Attributes
[0];
662 public override bool MoveToNextAttribute ()
667 if (current
.NodeType
!= XmlNodeType
.Attribute
)
668 return MoveToFirstAttribute ();
671 XmlAttributeCollection ac
= ((XmlAttribute
) current
).OwnerElement
.Attributes
;
672 for (int i
=0; i
<ac
.Count
-1; i
++)
674 XmlAttribute attr
= ac
[i
];
688 public override bool Read ()
690 // FIXME: at some stage inlining might work effectively.
693 case ReadState
.EndOfFile
:
694 case ReadState
.Error
:
695 case ReadState
.Closed
:
704 bool ret
= ReadContent ();
705 ownerLinkedNode
= current
;
711 if (ReadState
== ReadState
.Initial
) {
713 state
= ReadState
.Interactive
;
714 // when startNode is document or fragment
716 current
= startNode
.FirstChild
;
717 if (current
== null) {
718 state
= ReadState
.Error
;
726 // don't step into EntityReference's children. Also
727 // avoid re-entering children of already-consumed
728 // element (i.e. when it is regarded as EndElement).
730 !isEndElement
&& current
.NodeType
!= XmlNodeType
.EntityReference
?
731 current
.FirstChild
: null;
732 if (firstChild
!= null) {
733 isEndElement
= false;
734 current
= firstChild
;
739 if (current
== startNode
) { // Currently it is on the start node.
740 if (IsEmptyElement
|| isEndElement
) {
741 // The start node is already consumed.
742 isEndElement
= false;
744 state
= ReadState
.EndOfFile
;
747 // The start node is the only element
748 // which should be processed. Now it
749 // is set as EndElement.
754 if (!isEndElement
&& !IsEmptyElement
&&
755 current
.NodeType
== XmlNodeType
.Element
) {
756 // element, currently not EndElement, and has
757 // no child. (such as <foo></foo>, which
758 // should become EndElement).
763 // If NextSibling is available, move to there.
764 XmlNode next
= current
.NextSibling
;
766 isEndElement
= false;
771 // Otherwise, parent.
772 XmlNode parent
= current
.ParentNode
;
773 if (parent
== null || parent
== startNode
&& ignoreStartNode
) {
774 // Parent is not available, or reached to
775 // the start node. This reader never sets
776 // startNode as current if it was originally
777 // ignored (e.g. startNode is XmlDocument).
778 isEndElement
= false;
780 state
= ReadState
.EndOfFile
;
783 // Parent was available, so return it as
792 public override bool ReadAttributeValue ()
794 if (current
.NodeType
== XmlNodeType
.Attribute
) {
795 if (current
.FirstChild
== null)
797 current
= current
.FirstChild
;
799 } else if (current
.ParentNode
.NodeType
== XmlNodeType
.Attribute
) {
800 if (current
.NextSibling
== null)
802 current
= current
.NextSibling
;
808 public override string ReadString ()
810 return base.ReadString ();
813 #if !NET_2_1 || MONOTOUCH
814 public override void ResolveEntity ()
816 throw new NotSupportedException ("Should not happen.");
820 public override void Skip ()
822 // Why is this overriden? Such skipping might raise
823 // (or ignore) unexpected validation error.