bring a net_2_0 feature set to monotouch
[mcs.git] / class / System.XML / System.Xml / XmlNodeReaderImpl.cs
blobd1050baba456d0e591cc8f0d9a53786e49f96111
1 //
2 // System.Xml.XmlNodeReaderImpl.cs - implements the core part of XmlNodeReader
3 //
4 // Author:
5 // Atsushi Enomoto (atsushi@ximian.com)
6 //
7 // (C) 2004 Novell Inc.
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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
33 // ResolveEntity().
36 using System;
37 #if NET_2_0
38 using System.Collections.Generic;
39 #endif
40 using System.Xml;
41 using System.Text;
42 using Mono.Xml;
43 #if NET_2_0
44 using System.Xml.Schema;
45 #endif
47 namespace System.Xml
49 #if NET_2_0
50 internal class XmlNodeReaderImpl : XmlReader, IHasXmlParserContext, IXmlNamespaceResolver
51 #else
52 internal class XmlNodeReaderImpl : XmlReader, IHasXmlParserContext
53 #endif
55 XmlDocument document;
56 XmlNode startNode;
57 XmlNode current;
58 XmlNode ownerLinkedNode;
59 ReadState state = ReadState.Initial;
60 int depth;
61 bool isEndElement;
62 bool ignoreStartNode;
64 #region Constructor
66 internal XmlNodeReaderImpl (XmlNodeReaderImpl entityContainer)
67 : this (entityContainer.current)
71 public XmlNodeReaderImpl (XmlNode node)
73 startNode = node;
74 depth = 0;
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;
83 break;
88 #endregion
90 #region Properties
92 public override int AttributeCount {
93 get {
94 if (state != ReadState.Interactive)
95 return 0;
97 if (isEndElement || current == null)
98 return 0;
99 XmlNode n = ownerLinkedNode;
100 return n.Attributes != null ? n.Attributes.Count : 0;
104 public override string BaseURI {
105 get {
106 if (current == null)
107 return startNode.BaseURI;
108 return current.BaseURI;
112 #if NET_2_0
113 public override bool CanReadBinaryContent {
114 get { return true; }
117 public override bool CanReadValueChunk {
118 get { return true; }
120 #else
121 internal override bool CanReadBinaryContent {
122 get { return true; }
125 internal override bool CanReadValueChunk {
126 get { return true; }
128 #endif
130 public override bool CanResolveEntity {
131 get { return false; }
134 public override int Depth {
135 get {
136 return current == null ? 0 :
137 current == ownerLinkedNode ? depth :
138 current.NodeType == XmlNodeType.Attribute ? depth + 1 :
139 depth + 2;
143 public override bool EOF {
144 get { return state == ReadState.EndOfFile || state == ReadState.Error; }
147 public override bool HasAttributes {
148 get {
149 if (isEndElement || current == null)
150 return false;
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)
158 return false;
159 else
160 return true;
164 #if !NET_2_1 || MONOTOUCH
165 public override bool HasValue {
166 get {
167 if (current == null)
168 return false;
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:
178 return false;
179 default:
180 return true;
185 #endif
187 public override bool IsDefault {
188 get {
189 if (current == null)
190 return false;
192 if (current.NodeType != XmlNodeType.Attribute)
193 return false;
194 else
196 return !((XmlAttribute) current).Specified;
201 public override bool IsEmptyElement {
202 get {
203 if (current == null)
204 return false;
206 if(current.NodeType == XmlNodeType.Element)
207 return ((XmlElement) current).IsEmpty;
208 else
209 return false;
213 #if NET_2_0
214 #else
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); }
226 #endif
228 public override string LocalName {
229 get {
230 if (current == null)
231 return String.Empty;
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;
243 return String.Empty;
247 public override string Name {
248 get {
249 if (current == null)
250 return String.Empty;
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:
259 return current.Name;
262 return String.Empty;
266 public override string NamespaceURI {
267 get {
268 if (current == null)
269 return String.Empty;
271 return current.NamespaceURI;
275 public override XmlNameTable NameTable {
276 get { return document.NameTable; }
279 public override XmlNodeType NodeType {
280 get {
281 if (current == null)
282 return XmlNodeType.None;
284 return isEndElement ? XmlNodeType.EndElement : current.NodeType;
288 public override string Prefix {
289 get {
290 if (current == null)
291 return String.Empty;
293 return current.Prefix;
297 #if NET_2_0
298 #else
299 public override char QuoteChar {
300 get {
301 return '"';
304 #endif
306 public override ReadState ReadState {
307 get { return state; }
310 #if NET_2_0
311 public override IXmlSchemaInfo SchemaInfo {
312 get { return current != null ? current.SchemaInfo : null; }
314 #endif
316 public override string Value {
317 get {
318 if (NodeType == XmlNodeType.DocumentType)
319 return ((XmlDocumentType) current).InternalSubset;
320 else
321 return HasValue ? current.Value : String.Empty;
325 public override string XmlLang {
326 get {
327 if (current == null)
328 return startNode.XmlLang;
330 return current.XmlLang;
334 public override XmlSpace XmlSpace {
335 get {
336 if (current == null)
337 return startNode.XmlSpace;
339 return current.XmlSpace;
342 #endregion
344 #region Methods
346 public override void Close ()
348 current = null;
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)
357 return decl.Version;
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)
383 return 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)
395 return 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)
403 return null;
404 XmlAttribute attr = ownerLinkedNode.Attributes [name];
405 if (attr == null)
406 return null;
407 else
408 return attr.Value;
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)
415 return 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)
423 return null;
424 XmlAttribute attr = ownerLinkedNode.Attributes [name, namespaceURI];
425 if (attr == null)
426 return null; // In fact MS.NET returns null instead of String.Empty.
427 else
428 return attr.Value;
431 private string GetXmlDeclarationAttribute (string name)
433 XmlDeclaration decl = current as XmlDeclaration;
434 switch (name) {
435 case "version":
436 return decl.Version;
437 case "encoding":
438 // This is MS.NET bug that XmlNodeReturns in case of string.empty.
439 return decl.Encoding != String.Empty ? decl.Encoding : null;
440 case "standalone":
441 return decl.Standalone;
443 return null;
446 private string GetDocumentTypeAttribute (string name)
448 XmlDocumentType doctype = current as XmlDocumentType;
449 switch (name) {
450 case "PUBLIC":
451 return doctype.PublicId;
452 case "SYSTEM":
453 return doctype.SystemId;
455 return null;
458 XmlParserContext IHasXmlParserContext.ParserContext {
459 get {
460 return new XmlParserContext (document.NameTable,
461 current == null ?
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);
470 #if NET_2_0
471 public IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
473 IDictionary<string, string> table = new Dictionary<string, string> ();
474 XmlNode n = current;
475 do {
476 if (n.NodeType == XmlNodeType.Document)
477 break;
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)
484 return table;
485 n = n.ParentNode;
486 } while (n != null);
487 if (scope == XmlNamespaceScope.All)
488 table.Add (XmlNamespaceManager.PrefixXml, XmlNamespaceManager.XmlnsXml);
489 return table;
491 #endif
493 private XmlElement GetCurrentElement ()
495 XmlElement el = null;
496 switch (current.NodeType) {
497 case XmlNodeType.Attribute:
498 el = ((XmlAttribute) current).OwnerElement;
499 break;
500 case XmlNodeType.Element:
501 el = (XmlElement) current;
502 break;
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;
511 break;
513 return el;
516 public override string LookupNamespace (string prefix)
518 if (current == null)
519 return null;
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)
527 continue;
528 if (prefix == "") {
529 if (attr.Prefix == "")
530 return attr.Value;
532 else if (attr.LocalName == prefix)
533 return attr.Value;
534 continue;
538 switch (prefix) {
539 case XmlNamespaceManager.PrefixXml:
540 return XmlNamespaceManager.XmlnsXml;
541 case XmlNamespaceManager.PrefixXmlns:
542 return XmlNamespaceManager.XmlnsXmlns;
544 return null;
547 #if NET_2_0
548 public string LookupPrefix (string ns)
550 return LookupPrefix (ns, false);
553 public string LookupPrefix (string ns, bool atomizedNames)
555 if (current == null)
556 return null;
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];
563 if (atomizedNames) {
564 if (!Object.ReferenceEquals (attr.NamespaceURI, XmlNamespaceManager.XmlnsXmlns))
565 continue;
566 if (Object.ReferenceEquals (attr.Value, ns))
567 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
568 return attr.Prefix != String.Empty ? attr.LocalName : String.Empty;
569 } else {
570 if (attr.NamespaceURI != XmlNamespaceManager.XmlnsXmlns)
571 continue;
572 if (attr.Value == ns)
573 // xmlns:blah="..." -> LocalName, xmlns="..." -> String.Empty
574 return attr.Prefix != String.Empty ? attr.LocalName : String.Empty;
578 switch (ns) {
579 case XmlNamespaceManager.XmlnsXml:
580 return XmlNamespaceManager.PrefixXml;
581 case XmlNamespaceManager.XmlnsXmlns:
582 return XmlNamespaceManager.PrefixXmlns;
584 return null;
586 #endif
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)
600 return false;
601 XmlNode tmpCurrent = current;
602 if (current.ParentNode.NodeType == XmlNodeType.Attribute)
603 current = current.ParentNode;
605 if (ownerLinkedNode.Attributes == null)
606 return false;
607 XmlAttribute attr = ownerLinkedNode.Attributes [name];
608 if (attr == null) {
609 current = tmpCurrent;
610 return false;
612 else {
613 current = attr;
614 return true;
618 public override bool MoveToAttribute (string name, string namespaceURI)
620 if (isEndElement || current == null)
621 return false;
623 if (ownerLinkedNode.Attributes == null)
624 return false;
625 XmlAttribute attr = ownerLinkedNode.Attributes [name, namespaceURI];
626 if (attr == null)
627 return false;
628 else {
629 current = attr;
630 return true;
634 public override bool MoveToElement ()
636 if (current == null)
637 return false;
638 XmlNode n = ownerLinkedNode;
639 if (current != n) {
640 current = n;
641 return true;
642 } else
643 return false;
646 public override bool MoveToFirstAttribute ()
648 if (current == null)
649 return false;
651 if (ownerLinkedNode.Attributes == null)
652 return false;
653 if(ownerLinkedNode.Attributes.Count > 0)
655 current = ownerLinkedNode.Attributes [0];
656 return true;
658 else
659 return false;
662 public override bool MoveToNextAttribute ()
664 if (current == null)
665 return false;
667 if (current.NodeType != XmlNodeType.Attribute)
668 return MoveToFirstAttribute ();
669 else
671 XmlAttributeCollection ac = ((XmlAttribute) current).OwnerElement.Attributes;
672 for (int i=0; i<ac.Count-1; i++)
674 XmlAttribute attr = ac [i];
675 if (attr == current)
677 i++;
678 if (i == ac.Count)
679 return false;
680 current = ac [i];
681 return true;
684 return false;
688 public override bool Read ()
690 // FIXME: at some stage inlining might work effectively.
691 // if (EOF)
692 switch (state) {
693 case ReadState.EndOfFile:
694 case ReadState.Error:
695 case ReadState.Closed:
696 return false;
699 #if NET_2_0
700 if (Binary != null)
701 Binary.Reset ();
702 #endif
704 bool ret = ReadContent ();
705 ownerLinkedNode = current;
706 return ret;
709 bool ReadContent ()
711 if (ReadState == ReadState.Initial) {
712 current = startNode;
713 state = ReadState.Interactive;
714 // when startNode is document or fragment
715 if (ignoreStartNode)
716 current = startNode.FirstChild;
717 if (current == null) {
718 state = ReadState.Error;
719 return false;
720 } else
721 return true;
724 MoveToElement ();
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).
729 XmlNode firstChild =
730 !isEndElement && current.NodeType != XmlNodeType.EntityReference ?
731 current.FirstChild : null;
732 if (firstChild != null) {
733 isEndElement = false;
734 current = firstChild;
735 depth++;
736 return true;
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;
743 current = null;
744 state = ReadState.EndOfFile;
745 return false;
746 } else {
747 // The start node is the only element
748 // which should be processed. Now it
749 // is set as EndElement.
750 isEndElement = true;
751 return true;
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).
759 isEndElement = true;
760 return true;
763 // If NextSibling is available, move to there.
764 XmlNode next = current.NextSibling;
765 if (next != null) {
766 isEndElement = false;
767 current = next;
768 return true;
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;
779 current = null;
780 state = ReadState.EndOfFile;
781 return false;
782 } else {
783 // Parent was available, so return it as
784 // EndElement.
785 current = parent;
786 depth--;
787 isEndElement = true;
788 return true;
792 public override bool ReadAttributeValue ()
794 if (current.NodeType == XmlNodeType.Attribute) {
795 if (current.FirstChild == null)
796 return false;
797 current = current.FirstChild;
798 return true;
799 } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
800 if (current.NextSibling == null)
801 return false;
802 current = current.NextSibling;
803 return true;
804 } else
805 return false;
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.");
818 #endif
820 public override void Skip ()
822 // Why is this overriden? Such skipping might raise
823 // (or ignore) unexpected validation error.
824 base.Skip ();
826 #endregion