2003-03-19 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                 #region Constructor
22
23                 XmlNode startNode;
24                 XmlNode current;
25                 ReadState state = ReadState.Initial;
26                 int depth;
27                 bool isEndElement;
28                 bool isEndEntity;
29                 bool nextIsEndElement;  // used for ReadString()
30                 bool alreadyRead;
31
32                 public XmlNodeReader (XmlNode node)
33                 {
34                         startNode = node;
35                         if (node.NodeType != XmlNodeType.Document
36                                 && node.NodeType != XmlNodeType.DocumentFragment)
37                                 alreadyRead = true;
38                 }
39                 
40                 #endregion
41
42                 #region Properties
43
44                 public override int AttributeCount {
45                         get {
46                                 if (current == null)
47                                         return 0;
48
49                                 return ((ICollection) current.Attributes).Count;
50                         }
51                 }
52
53                 public override string BaseURI {
54                         get { 
55                                 if (current == null)
56                                         return String.Empty;
57                                 return current.BaseURI;
58                         }
59                 }
60
61                 [MonoTODO("wait for XML resolver")]
62                 public override bool CanResolveEntity {
63                         get {
64                                 throw new NotImplementedException ();
65                         }
66                 }
67
68                 public override int Depth {
69                         get { return depth; }
70                 }
71
72                 public override bool EOF {
73                         get {
74                                 return this.ReadState == ReadState.EndOfFile 
75                                 || this.ReadState == ReadState.Error;
76                         }
77                 }
78
79                 public override bool HasAttributes {
80                         get {
81                                 if (current == null)
82                                         return false;
83
84                                 if (current.Attributes == null)
85                                         return false;
86                                 else
87                                         return true;
88                         }
89                 }
90
91                 public override bool HasValue {
92                         get {
93                                 if (current == null)
94                                         return false;
95
96                                 if (current.NodeType == XmlNodeType.Element ||
97                                     current.NodeType == XmlNodeType.EntityReference ||
98                                     current.NodeType == XmlNodeType.Document ||
99                                     current.NodeType == XmlNodeType.DocumentFragment ||
100                                     current.NodeType == XmlNodeType.Notation ||
101                                     current.NodeType == XmlNodeType.EndElement ||
102                                     current.NodeType == XmlNodeType.EndEntity)
103                                         return false;
104                                 else
105                                         return true;
106                         }
107                               
108                 }
109
110                 [MonoTODO("waiting for DTD implementation")]
111                 public override bool IsDefault {
112                         get {
113                                 if (current == null)
114                                         return false;
115
116                                 if (current.NodeType != XmlNodeType.Attribute)
117                                         return false;
118                                 else
119                                 {
120                                         return ((XmlAttribute) current).isDefault;
121                                 }
122                         }
123                 }
124
125                 [MonoTODO("test it.")]
126                 public override bool IsEmptyElement {
127                         get {
128                                 if (current == null)
129                                         return false;
130
131                                 if(current.NodeType == XmlNodeType.Element)
132                                         return ((XmlElement) current).IsEmpty;
133                                 else 
134                                         return false;
135                         }
136                 }
137
138                 public override string this [int i] {
139                         get {
140                                 if (current == null)
141                                         return null;
142
143                                 if (i < 0 || i > AttributeCount)
144                                         throw new ArgumentOutOfRangeException ("i is out of range.");
145                                 
146                                 return current.Attributes [i].Value;
147                         }
148                 }
149
150                 public override string this [string name] {
151                         get {
152                                 if (current == null)
153                                         return null;
154
155                                 string ret =  current.Attributes [name].Value;
156                                 
157                                 if (ret == null)
158                                         return String.Empty;
159                                 else
160                                         return ret;
161                         }
162                 }
163
164                 public override string this [string name, string namespaceURI] {
165                         get {
166                                 if (current == null)
167                                         return null;
168
169                                 string ret =  current.Attributes [name, namespaceURI].Value;
170                                 
171                                 if (ret == null)
172                                         return String.Empty;
173                                 else
174                                         return ret;
175                         }
176                 }
177
178                 public override string LocalName {
179                         get {
180                                 if (current == null)
181                                         return String.Empty;
182
183                                 if (current is XmlCharacterData)
184                                         return String.Empty;
185                                 else
186                                         return current.LocalName;
187                         }
188                 }
189
190                 public override string Name {
191                         get {
192                                 if (current == null)
193                                         return String.Empty;
194
195                                 return current.Name;
196                         }
197                 }
198
199                 public override string NamespaceURI {
200                         get {
201                                 if (current == null)
202                                         return String.Empty;
203
204                                 return current.NamespaceURI;
205                         }
206                 }
207
208                 public override XmlNameTable NameTable {
209                         get {
210                                 XmlDocument doc = 
211                                         current.NodeType == XmlNodeType.Document ?
212                                         current as XmlDocument : current.OwnerDocument;
213                                 return doc.NameTable;
214                         }
215                 }
216
217                 public override XmlNodeType NodeType {
218                         get {
219                                 if (current == null)
220                                         return XmlNodeType.None;
221
222                                 return isEndElement ? XmlNodeType.EndElement : current.NodeType;
223                         }
224                 }
225
226                 public override string Prefix {
227                         get { 
228                                 if (current == null)
229                                         return String.Empty;
230
231                                 return current.Prefix;
232                         }
233                 }
234
235                 public override char QuoteChar {
236                         get { return '"'; }
237                 }
238
239                 public override ReadState ReadState {
240                         get { return state; }
241                 }
242
243                 public override string Value {
244                         get {
245                                 return HasValue ? current.Value : String.Empty;
246                         }
247                 }
248
249                 public override string XmlLang {
250                         get {
251                                 if (current == null)
252                                         return String.Empty;
253
254                                 return current.XmlLang;
255                         }
256                 }
257
258                 public override XmlSpace XmlSpace {
259                         get {
260                                 if (current == null)
261                                         return XmlSpace.None;
262
263                                 return current.XmlSpace;
264                         }
265                 }
266                 #endregion
267
268                 #region Methods
269
270                 public override void Close ()
271                 {
272                         current = null;
273                         state = ReadState.Closed;
274                 }
275
276                 public override string GetAttribute (int attributeIndex)
277                 {
278                         return this [attributeIndex];
279                 }
280
281                 public override string GetAttribute (string name)
282                 {
283                         return this [name];
284                 }
285
286                 public override string GetAttribute (string name, string namespaceURI)
287                 {
288                         return this [name, namespaceURI];
289                 }
290
291                 // FIXME: Its performance is not good.
292                 public override string LookupNamespace (string prefix)
293                 {
294                         XmlNamespaceManager nsmgr = current.ConstructNamespaceManager();
295                         return nsmgr.LookupNamespace (prefix);
296                 }
297
298                 public override void MoveToAttribute (int attributeIndex)
299                 {
300                         if (attributeIndex < 0 || attributeIndex > AttributeCount)
301                                 throw new ArgumentOutOfRangeException ();
302                         
303                         state = ReadState.Interactive;
304                         current = current.Attributes [attributeIndex];
305                 }
306
307                 public override bool MoveToAttribute (string name)
308                 {
309                         if (GetAttribute (name) == null)
310                                 return false;
311                         else {
312                                 current = current.Attributes [name];
313                                 return true;
314                         }
315                 }
316
317                 public override bool MoveToAttribute (string name, string namespaceURI)
318                 {
319                         if (GetAttribute (name, namespaceURI) == null)
320                                 return false;
321                         else {
322                                 current = current.Attributes [name, namespaceURI];
323                                 return true;
324                         }
325                 }
326
327                 private void MoveToEndElement ()
328                 {
329                         isEndElement = true;
330                         depth--;
331                         current = current.ParentNode;
332                 }
333
334                 public override bool MoveToElement ()
335                 {
336                         if (current == null)
337                                 return false;
338                         if (current.NodeType == XmlNodeType.Attribute) {
339                                 current = ((XmlAttribute) current).OwnerElement;
340                                 return true;
341                         } else 
342                                 return false;
343                 }
344
345                 public override bool MoveToFirstAttribute ()
346                 {
347                         if(current.Attributes.Count > 0)
348                         {
349                                 current = current.Attributes [0];
350                                 return true;
351                         }
352                         else
353                                 return false;
354                 }
355
356                 public override bool MoveToNextAttribute ()
357                 {
358                         if (current.NodeType != XmlNodeType.Attribute)
359                                 return MoveToFirstAttribute ();
360                         else
361                         {
362                                 XmlAttributeCollection ac = ((XmlAttribute) current).OwnerElement.Attributes;
363                                 for (int i=0; i<ac.Count-1; i++)
364                                 {
365                                         XmlAttribute attr = ac [i];
366                                         if (attr == current)
367                                         {
368                                                 current = ac [i+1];
369                                                 return true;
370                                         }
371                                 }
372                                 return false;
373                         }
374                 }
375
376                 private bool MoveToNextSibling ()
377                 {
378                         if (nextIsEndElement) {
379                                 // nextIsEndElement is set only by ReadString.
380                                 nextIsEndElement = false;
381                                 MoveToEndElement ();
382                         } else if (alreadyRead) {
383                                 alreadyRead = false;
384                                 return current != null;
385                         }
386                         if (current.NextSibling != null) {
387                                 isEndElement = false;
388                                 current = current.NextSibling;
389                         } else {
390                                 MoveToEndElement ();
391                         }
392                         return current != null;
393                 }
394
395                 [MonoTODO("Entity handling is not supported.")]
396                 public override bool Read ()
397                 {
398                         if (EOF)
399                                 return false;
400
401                         if (ReadState == ReadState.Initial) {
402                                 current = startNode;
403                                 state = ReadState.Interactive;
404                                 // when startNode is document or fragment
405                                 if (!alreadyRead)
406                                         current = startNode.FirstChild;
407                                 else
408                                         alreadyRead = false;
409                                 if (current == null) {
410                                         state = ReadState.Error;
411                                         return false;
412                                 } else
413                                         return true;
414                         }
415
416                         MoveToElement ();
417                         isEndEntity = false;
418
419                         if (isEndElement) {
420                                 // Then go up and move to next.
421                                 // If no more nodes, then set EOF.
422                                 isEndElement = false;
423                                 if (current.ParentNode == null
424                                         || current.ParentNode.NodeType == XmlNodeType.Document
425                                         || current.ParentNode.NodeType == XmlNodeType.DocumentFragment) {
426                                         current = null;
427                                         state = ReadState.EndOfFile;
428                                         return false;
429                                 } else if (current.NextSibling == null) {
430                                         depth--;
431                                         current = current.ParentNode;
432                                         isEndElement = true;
433                                         return true;
434                                 } else {
435                                         current = current.NextSibling;
436                                         return true;
437                                 }
438
439                         } else if (nextIsEndElement) {
440                                 // nextIsEndElement is set only by ReadString.
441                                 nextIsEndElement = false;
442                                 isEndElement = true;
443                                 return current != null;
444
445                         } else if (alreadyRead) {
446                                 alreadyRead = false;
447                                 return current != null;
448                         }
449
450                         // hmm... here may be unnecessary codes. plz check anyone ;)
451                         if (!isEndElement && current.FirstChild != null) {
452                                 isEndElement = false;
453                                 current = current.FirstChild;
454                                 depth++;
455                         } else if (depth == 0) {
456                                 state = ReadState.EndOfFile;
457                                 return false;
458                         } else
459                                 MoveToNextSibling ();
460
461                         return current != null;
462                 }
463
464                 public override bool ReadAttributeValue ()
465                 {
466                         if (current.NodeType == XmlNodeType.Attribute) {
467                                 current = current.FirstChild;
468                                 return current != null;
469                         } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
470                                 current = current.NextSibling;
471                                 return current != null;
472                         } else
473                                 return false;
474                 }
475
476                 [MonoTODO("Need to move to next content.")]
477                 // Its traversal behavior is almost same as Read().
478                 public override string ReadInnerXml ()
479                 {
480                         if (ReadState == ReadState.Initial) {
481                                 state = ReadState.Error;
482                                 return String.Empty;    // heh
483                         }
484                 
485                         if (current.NodeType != XmlNodeType.Attribute &&
486                             current.NodeType != XmlNodeType.Element)
487                                 return String.Empty;
488                         else
489                                 return current.InnerXml;
490                 }
491
492                 [MonoTODO("Need to move to next content.")]
493                 // Its traversal behavior is almost same as Read().
494                 public override string ReadOuterXml ()
495                 {
496                         if (NodeType == XmlNodeType.EndElement)
497                                 return String.Empty;
498
499                         if (current.NodeType != XmlNodeType.Attribute &&
500                             current.NodeType != XmlNodeType.Element)
501                                 return String.Empty;
502                         else
503                                 return current.OuterXml;
504                 }
505
506                 public override string ReadString ()
507                 {
508                         if (NodeType == XmlNodeType.EndElement)
509                                 return String.Empty;
510
511                         XmlNode original = current;
512                         StringBuilder builder = new StringBuilder();
513                         if (NodeType == XmlNodeType.Element) {
514                                 foreach (XmlNode child in current.ChildNodes) {
515                                         if (child is XmlCharacterData && !(child is XmlComment))
516                                                 builder.Append (child.Value);
517                                         else {
518                                                 depth++;
519                                                 current = child;
520                                                 break;
521                                         }
522                                 }
523                                 alreadyRead = true;
524                                 if (current == original) {
525                                         nextIsEndElement = true;
526                                         Read ();
527                                 }
528                         } else {
529                                 do {
530                                         builder.Append (current.Value);
531                                         if (current.NextSibling == null) {
532                                                 nextIsEndElement = true;
533                                                 break;
534                                         } else if (current.NextSibling.NodeType == XmlNodeType.Comment)
535                                                 break;
536                                         else
537                                                 current = current.NextSibling;
538                                 } while (true);
539                                 alreadyRead = true;
540                                 if (current.NextSibling == null) {
541                                         nextIsEndElement = true;
542                                         Read ();
543                                 }
544                         }
545                         return builder.ToString ();
546                 }
547
548                 [MonoTODO]
549                 public override void ResolveEntity ()
550                 {
551                         throw new NotImplementedException ();
552 //                      if (current.NodeType != XmlNodeType.EntityReference)
553 //                              throw new InvalidOperationException ("The current node is not an Entity Reference");
554                 }
555
556                 [MonoTODO("test it.")]
557                 public override void Skip ()
558                 {
559                         MoveToElement ();
560                         if(current.ChildNodes.Count > 0)
561                                 MoveToNextSibling ();
562                         else
563                                 Read ();
564                 }
565                 #endregion
566         }
567 }