2003-07-24 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[mono.git] / mcs / class / System.XML / System.Xml / XmlNodeReader.cs
1 //
2 // System.Xml.XmlNodeReader.cs
3 //
4 // Author:
5 //      Duncan Mak  (duncan@ximian.com)
6 //      Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
7 //
8 // (C) Ximian, Inc.
9 // (C) Atsushi Enomoto
10 //
11
12 using System;
13 using System.Collections;
14 using System.Xml;
15 using System.Text;
16
17 namespace System.Xml
18 {
19         public class XmlNodeReader : XmlReader
20         {
21                 XmlDocument document;
22                 XmlNode startNode;
23                 XmlNode current;
24                 ReadState state = ReadState.Initial;
25                 int depth;
26                 bool isEndElement;
27                 bool isEndEntity;
28                 bool nextIsEndElement;  // used for ReadString()
29                 bool alreadyRead;
30                 StringBuilder valueBuilder = new StringBuilder ();
31                 XmlNamespaceManager defaultNsmgr;
32
33                 private XmlNode ownerElement {
34                         get {
35                                 return (current.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)current).OwnerElement : current;
36                         }
37                 }
38
39                 #region Constructor
40
41                 public XmlNodeReader (XmlNode node)
42                 {
43                         startNode = node;
44                         document = startNode.NodeType == XmlNodeType.Document ?
45                                 startNode as XmlDocument : startNode.OwnerDocument;
46
47                         if (node.NodeType != XmlNodeType.Document
48                                 && node.NodeType != XmlNodeType.DocumentFragment)
49                                 alreadyRead = true;
50                         defaultNsmgr = new XmlNamespaceManager (this.NameTable);
51
52                 }
53                 
54                 #endregion
55
56                 #region Properties
57
58                 public override int AttributeCount {
59                         get {
60                                 if (isEndElement || current == null || current.Attributes == null)
61                                         return 0;
62                                 return ownerElement.Attributes.Count;
63                         }
64                 }
65
66                 public override string BaseURI {
67                         get { 
68                                 if (current == null)
69                                         return String.Empty;
70                                 return current.BaseURI;
71                         }
72                 }
73
74                 [MonoTODO("wait for XML resolver")]
75                 public override bool CanResolveEntity {
76                         get {
77                                 throw new NotImplementedException ();
78                         }
79                 }
80
81                 public override int Depth {
82                         get { return depth; }
83                 }
84
85                 public override bool EOF {
86                         get {
87                                 return this.ReadState == ReadState.EndOfFile 
88                                 || this.ReadState == ReadState.Error;
89                         }
90                 }
91
92                 public override bool HasAttributes {
93                         get {
94                                 if (isEndElement || current == null)
95                                         return false;
96
97                                 if (current.Attributes == null ||
98                                         current.Attributes.Count == 0)
99                                         return false;
100                                 else
101                                         return true;
102                         }
103                 }
104
105                 public override bool HasValue {
106                         get {
107                                 if (current == null)
108                                         return false;
109
110                                 if (current.NodeType == XmlNodeType.Element ||
111                                     current.NodeType == XmlNodeType.EntityReference ||
112                                     current.NodeType == XmlNodeType.Document ||
113                                     current.NodeType == XmlNodeType.DocumentFragment ||
114                                     current.NodeType == XmlNodeType.Notation ||
115                                     current.NodeType == XmlNodeType.EndElement ||
116                                     current.NodeType == XmlNodeType.EndEntity)
117                                         return false;
118                                 else
119                                         return true;
120                         }
121                               
122                 }
123
124                 [MonoTODO("waiting for DTD implementation")]
125                 public override bool IsDefault {
126                         get {
127                                 if (current == null)
128                                         return false;
129
130                                 if (current.NodeType != XmlNodeType.Attribute)
131                                         return false;
132                                 else
133                                 {
134                                         return ((XmlAttribute) current).isDefault;
135                                 }
136                         }
137                 }
138
139                 public override bool IsEmptyElement {
140                         get {
141                                 if (current == null)
142                                         return false;
143
144                                 if(current.NodeType == XmlNodeType.Element)
145                                         return ((XmlElement) current).IsEmpty;
146                                 else 
147                                         return false;
148                         }
149                 }
150
151                 public override string this [int i] {
152                         get {
153                                 // This is MS.NET bug which returns attributes in spite of EndElement.
154                                 if (isEndElement || current == null)
155                                         return null;
156
157                                 if (NodeType == XmlNodeType.XmlDeclaration) {
158                                         XmlDeclaration decl = current as XmlDeclaration;
159                                         switch (i) {
160                                         case 0:
161                                                 return decl.Version;
162                                         case 1:
163                                                 if (decl.Encoding != String.Empty)
164                                                         return decl.Encoding;
165                                                 else if (decl.Standalone != String.Empty)
166                                                         return decl.Standalone;
167                                                 else
168                                                         throw new ArgumentOutOfRangeException ("Index out of range.");
169                                         case 2:
170                                                 if (decl.Encoding != String.Empty && decl.Standalone != null)
171                                                         return decl.Standalone;
172                                                 else
173                                                         throw new ArgumentOutOfRangeException ("Index out of range.");
174                                         }
175                                 }
176
177                                 if (i < 0 || i > AttributeCount)
178                                         throw new ArgumentOutOfRangeException ("Index out of range.");
179
180                                 return ownerElement.Attributes [i].Value;
181                         }
182                 }
183
184                 private string GetXmlDeclarationAttribute (string name)
185                 {
186                         XmlDeclaration decl = current as XmlDeclaration;
187                         switch (name) {
188                         case "version":
189                                 return decl.Version;
190                         case "encoding":
191                                 return decl.Encoding;
192                         case "standalone":
193                                 return decl.Standalone;
194                         }
195                         return null;
196                 }
197
198                 private string GetDocumentTypeAttribute (string name)
199                 {
200                         XmlDocumentType doctype = current as XmlDocumentType;
201                         switch (name) {
202                         case "PUBLIC":
203                                 return doctype.PublicId;
204                         case "SYSTEM":
205                                 return doctype.SystemId;
206                         }
207                         return null;
208                 }
209
210                 public override string this [string name] {
211                         get {
212                                 // This is MS.NET bug which returns attributes in spite of EndElement.
213                                 if (isEndElement || current == null)
214                                         return null;
215
216                                 if (NodeType == XmlNodeType.XmlDeclaration)
217                                         return GetXmlDeclarationAttribute (name);
218                                 else if (NodeType == XmlNodeType.DocumentType)
219                                         return GetDocumentTypeAttribute (name);
220
221                                 XmlAttribute attr = ownerElement.Attributes [name];
222                                 if (attr == null)
223                                         return null;
224                                 else
225                                         return attr.Value;
226                         }
227                 }
228
229                 public override string this [string name, string namespaceURI] {
230                         get {
231                                 // This is MS.NET bug which returns attributes in spite of EndElement.
232                                 if (isEndElement || current == null)
233                                         return null;
234
235                                 if (NodeType == XmlNodeType.XmlDeclaration)
236                                         return GetXmlDeclarationAttribute (name);
237
238                                 XmlAttribute attr = ownerElement.Attributes [name, namespaceURI];
239                                 if (attr == null)
240                                         return null;    // In fact MS.NET returns null instead of String.Empty.
241                                 else
242                                         return attr.Value;
243                         }
244                 }
245
246                 public override string LocalName {
247                         get {
248                                 if (current == null)
249                                         return String.Empty;
250
251                                 switch (current.NodeType) {
252                                 case XmlNodeType.Attribute:
253                                 case XmlNodeType.DocumentType:
254                                 case XmlNodeType.Element:
255                                 case XmlNodeType.EntityReference:
256                                 case XmlNodeType.ProcessingInstruction:
257                                 case XmlNodeType.XmlDeclaration:
258                                         return current.LocalName;
259                                 }
260
261                                 return String.Empty;
262                         }
263                 }
264
265                 public override string Name {
266                         get {
267                                 if (current == null)
268                                         return String.Empty;
269
270                                 switch (current.NodeType) {
271                                 case XmlNodeType.Attribute:
272                                 case XmlNodeType.DocumentType:
273                                 case XmlNodeType.Element:
274                                 case XmlNodeType.EntityReference:
275                                 case XmlNodeType.ProcessingInstruction:
276                                 case XmlNodeType.XmlDeclaration:
277                                         return current.Name;
278                                 }
279
280                                 return String.Empty;
281                         }
282                 }
283
284                 public override string NamespaceURI {
285                         get {
286                                 if (current == null)
287                                         return String.Empty;
288
289                                 return current.NamespaceURI;
290                         }
291                 }
292
293                 public override XmlNameTable NameTable {
294                         get { return document.NameTable; }
295                 }
296
297                 public override XmlNodeType NodeType {
298                         get {
299                                 if (current == null)
300                                         return XmlNodeType.None;
301
302                                 return isEndElement ? XmlNodeType.EndElement : current.NodeType;
303                         }
304                 }
305
306                 public override string Prefix {
307                         get { 
308                                 if (current == null)
309                                         return String.Empty;
310
311                                 return current.Prefix;
312                         }
313                 }
314
315                 public override char QuoteChar {
316                         get { return '"'; }
317                 }
318
319                 public override ReadState ReadState {
320                         get { return state; }
321                 }
322
323                 public override string Value {
324                         get {
325                                 if (NodeType == XmlNodeType.DocumentType)
326                                         return ((XmlDocumentType) current).InternalSubset;
327                                 else
328                                         return HasValue ? current.Value : String.Empty;
329                         }
330                 }
331
332                 public override string XmlLang {
333                         get {
334                                 if (current == null)
335                                         return String.Empty;
336
337                                 return current.XmlLang;
338                         }
339                 }
340
341                 public override XmlSpace XmlSpace {
342                         get {
343                                 if (current == null)
344                                         return XmlSpace.None;
345
346                                 return current.XmlSpace;
347                         }
348                 }
349                 #endregion
350
351                 #region Methods
352
353                 public override void Close ()
354                 {
355                         current = null;
356                         state = ReadState.Closed;
357                 }
358
359                 public override string GetAttribute (int attributeIndex)
360                 {
361                         return this [attributeIndex];
362                 }
363
364                 public override string GetAttribute (string name)
365                 {
366                         return this [name];
367                 }
368
369                 public override string GetAttribute (string name, string namespaceURI)
370                 {
371                         return this [name, namespaceURI];
372                 }
373
374                 public override string LookupNamespace (string prefix)
375                 {
376                         if (current == null)
377                                 return null;
378
379                         XmlAttribute curAttr = current as XmlAttribute;
380                         XmlNode target = curAttr != null ? curAttr.OwnerElement : current;
381
382                         if (prefix == "") {
383                                 do {
384                                         XmlAttribute attr = target.Attributes ["xmlns"];
385                                         if (attr != null)
386                                                 return attr.Value;
387                                         target = target.ParentNode;
388                                 } while (target.NodeType != XmlNodeType.Document);
389                         } else {
390                                 string name = "xmlns:" + prefix;
391                                 do {
392                                         XmlAttribute attr = target.Attributes [name];
393                                         if (attr != null)
394                                                 return attr.Value;
395                                         target = target.ParentNode;
396                                 } while (target.NodeType != XmlNodeType.Document);
397                         }
398                         return defaultNsmgr.LookupNamespace (prefix);
399                 }
400
401                 public override void MoveToAttribute (int attributeIndex)
402                 {
403                         if (isEndElement || attributeIndex < 0 || attributeIndex > AttributeCount)
404                                 throw new ArgumentOutOfRangeException ();
405                         
406                         state = ReadState.Interactive;
407                         current = ownerElement.Attributes [attributeIndex];
408                 }
409
410                 public override bool MoveToAttribute (string name)
411                 {
412                         if (isEndElement || current == null)
413                                 return false;
414                         XmlNode tmpCurrent = current;
415                         if (current.ParentNode.NodeType == XmlNodeType.Attribute)
416                                 current = current.ParentNode;
417
418                         XmlAttribute attr = ownerElement.Attributes [name];
419                         if (attr == null) {
420                                 current = tmpCurrent;
421                                 return false;
422                         }
423                         else {
424                                 current = attr;
425                                 return true;
426                         }
427                 }
428
429                 public override bool MoveToAttribute (string name, string namespaceURI)
430                 {
431                         if (isEndElement || current == null)
432                                 return false;
433
434                         XmlAttribute attr = ownerElement.Attributes [name, namespaceURI];
435                         if (attr == null)
436                                 return false;
437                         else {
438                                 current = attr;
439                                 return true;
440                         }
441                 }
442
443                 private void MoveToParentElement ()
444                 {
445                         // This is buggy. It is not only the case when EndElement = true.
446                         isEndElement = true;
447                         depth--;
448                         current = current.ParentNode;
449                 }
450
451                 public override bool MoveToElement ()
452                 {
453                         if (current == null)
454                                 return false;
455                         if (current.NodeType == XmlNodeType.Attribute) {
456                                 current = ((XmlAttribute) current).OwnerElement;
457                                 return true;
458                         } else 
459                                 return false;
460                 }
461
462                 public override bool MoveToFirstAttribute ()
463                 {
464                         if (current == null)
465                                 return false;
466
467                         if(ownerElement.Attributes.Count > 0)
468                         {
469                                 current = ownerElement.Attributes [0];
470                                 return true;
471                         }
472                         else
473                                 return false;
474                 }
475
476                 public override bool MoveToNextAttribute ()
477                 {
478                         if (current == null)
479                                 return false;
480
481                         if (current.NodeType != XmlNodeType.Attribute)
482                                 return MoveToFirstAttribute ();
483                         else
484                         {
485                                 XmlAttributeCollection ac = ((XmlAttribute) current).OwnerElement.Attributes;
486                                 for (int i=0; i<ac.Count-1; i++)
487                                 {
488                                         XmlAttribute attr = ac [i];
489                                         if (attr == current)
490                                         {
491                                                 i++;
492                                                 if (i == ac.Count)
493                                                         return false;
494                                                 current = ac [i];
495                                                 return true;
496                                         }
497                                 }
498                                 return false;
499                         }
500                 }
501
502                 private bool MoveToNextSibling ()
503                 {
504                         if (nextIsEndElement) {
505                                 // nextIsEndElement is set only by ReadString.
506                                 nextIsEndElement = false;
507                                 MoveToParentElement ();
508                         } else if (alreadyRead) {
509                                 alreadyRead = false;
510                                 return current != null;
511                         }
512                         if (current.NextSibling != null) {
513                                 isEndElement = false;
514                                 current = current.NextSibling;
515                         } else {
516                                 MoveToParentElement ();
517                         }
518                         if (current == null) {
519                                 state = ReadState.EndOfFile;
520                                 return false;
521                         }
522                         else
523                                 return true;
524                 }
525
526                 [MonoTODO("Entity handling is not supported.")]
527                 public override bool Read ()
528                 {
529                         if (EOF)
530                                 return false;
531
532                         if (ReadState == ReadState.Initial) {
533                                 current = startNode;
534                                 state = ReadState.Interactive;
535                                 // when startNode is document or fragment
536                                 if (!alreadyRead)
537                                         current = startNode.FirstChild;
538                                 else
539                                         alreadyRead = false;
540                                 if (current == null) {
541                                         state = ReadState.Error;
542                                         return false;
543                                 } else
544                                         return true;
545                         }
546
547                         MoveToElement ();
548                         isEndEntity = false;
549
550                         if (IsEmptyElement || isEndElement) {
551                                 // Then go up and move to next.
552                                 // If no more nodes, then set EOF.
553                                 isEndElement = false;
554                                 if (current.ParentNode == null
555                                         || current.ParentNode.NodeType == XmlNodeType.Document
556                                         || current.ParentNode.NodeType == XmlNodeType.DocumentFragment) {
557                                         current = null;
558                                         state = ReadState.EndOfFile;
559                                         return false;
560                                 } else if (current.NextSibling == null) {
561                                         depth--;
562                                         current = current.ParentNode;
563                                         isEndElement = true;
564                                         return true;
565                                 } else {
566                                         current = current.NextSibling;
567                                         return true;
568                                 }
569
570                         } else if (nextIsEndElement) {
571                                 // nextIsEndElement is set only by ReadString.
572                                 nextIsEndElement = false;
573                                 isEndElement = true;
574                                 return current != null;
575
576                         } else if (alreadyRead) {
577                                 alreadyRead = false;
578                                 return current != null;
579                         }
580
581                         if (!isEndElement && current.FirstChild != null) {
582                                 isEndElement = false;
583                                 current = current.FirstChild;
584                                 depth++;
585                         } else if (current.NodeType == XmlNodeType.Element) {
586                                 isEndElement = true;
587                                 if (current.FirstChild != null)
588                                         depth--;
589                         } else
590                                 MoveToNextSibling ();
591
592                         return current != null;
593                 }
594
595                 public override bool ReadAttributeValue ()
596                 {
597                         if (current.NodeType == XmlNodeType.Attribute) {
598                                 current = current.FirstChild;
599                                 return current != null;
600                         } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
601                                 if (current.NextSibling == null)
602                                         return false;
603                                 current = current.NextSibling;
604                                 return true;
605                         } else
606                                 return false;
607                 }
608
609                 // Its traversal behavior is almost same as Read().
610                 public override string ReadInnerXml ()
611                 {
612                         if (this.state != ReadState.Interactive)
613                                 return String.Empty;
614
615                         XmlNode initial = current;
616                         // Almost copied from XmlTextReader.
617                         switch (NodeType) {
618                         case XmlNodeType.Attribute:
619                                 return Value;
620                         case XmlNodeType.Element:
621                                 if (IsEmptyElement)
622                                         return String.Empty;
623
624                                 int startDepth = depth;
625
626                                 bool loop = true;
627                                 do {
628                                         Read ();
629                                         if (NodeType ==XmlNodeType.None)
630                                                 throw new XmlException ("unexpected end of xml.");
631                                         else if (NodeType == XmlNodeType.EndElement && depth == startDepth) {
632                                                 loop = false;
633                                                 Read ();
634                                         }
635                                 } while (loop);
636                                 return initial.InnerXml;
637                         case XmlNodeType.None:
638                                 return String.Empty;
639                         default:
640                                 Read ();
641                                 return String.Empty;
642                         }
643                 }
644
645                 [MonoTODO("Need to move to next content.")]
646                 // Its traversal behavior is almost same as Read().
647                 public override string ReadOuterXml ()
648                 {
649                         if (NodeType == XmlNodeType.EndElement)
650                                 return String.Empty;
651                         XmlNode initial = current;
652
653                         switch (NodeType) {
654                         case XmlNodeType.Attribute:
655                                 return current.OuterXml;
656                         case XmlNodeType.Element:
657                                 if (NodeType == XmlNodeType.Element && !IsEmptyElement)
658                                         ReadInnerXml ();
659                                 else
660                                         Read ();
661                                 return initial.OuterXml;
662                         case XmlNodeType.None:
663                                 return String.Empty;
664                         default:
665                                 Read ();
666                                 return String.Empty;
667                         }
668                 }
669
670                 public override string ReadString ()
671                 {
672                         return ReadStringInternal ();
673                 }
674
675                 [MonoTODO]
676                 public override void ResolveEntity ()
677                 {
678                         throw new NotImplementedException ();
679 //                      if (current.NodeType != XmlNodeType.EntityReference)
680 //                              throw new InvalidOperationException ("The current node is not an Entity Reference");
681                 }
682
683                 [MonoTODO("test it.")]
684                 public override void Skip ()
685                 {
686                         // Why is this overriden? Such skipping might raise
687                         // (or ignore) unexpected validation error.
688                         base.Skip ();
689                 }
690                 #endregion
691         }
692 }