2003-04-25 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocument.cs
1 //
2 // System.Xml.XmlDocument
3 //
4 // Authors:
5 //   Daniel Weber (daniel-weber@austin.rr.com)
6 //   Kral Ferch <kral_ferch@hotmail.com>
7 //   Jason Diamond <jason@injektilo.org>
8 //   Miguel de Icaza (miguel@ximian.com)
9 //   Duncan Mak (duncan@ximian.com)
10 //   Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
11 //
12 // (C) 2001 Daniel Weber
13 // (C) 2002 Kral Ferch, Jason Diamond, Miguel de Icaza, Duncan Mak,
14 //   Atsushi Enomoto
15 //
16
17 using System;
18 using System.IO;
19 using System.Text;
20 using System.Xml.XPath;
21 using System.Diagnostics;
22 using System.Collections;
23 using Mono.Xml;
24 using Mono.Xml.Native;
25
26 namespace System.Xml
27 {
28         public class XmlDocument : XmlNode
29         {
30                 #region Fields
31
32                 XmlLinkedNode lastLinkedChild;
33                 XmlNameTable nameTable;
34                 string baseURI = String.Empty;
35                 XmlImplementation implementation;
36                 bool preserveWhitespace = false;
37                 WeakReference reusableXmlTextReader;
38
39                 #endregion
40
41                 #region Constructors
42
43                 public XmlDocument () : this (null, null)
44                 {
45                 }
46
47                 protected internal XmlDocument (XmlImplementation imp) : this (imp, null)
48                 {
49                 }
50
51                 public XmlDocument (XmlNameTable nt) : this (null, nt)
52                 {
53                 }
54
55                 XmlDocument (XmlImplementation impl, XmlNameTable nt) : base (null)
56                 {
57                         implementation = (impl != null) ? impl : new XmlImplementation ();
58                         nameTable = (nt != null) ? nt : implementation.internalNameTable;
59                         AddDefaultNameTableKeys ();
60                 }
61                 #endregion
62
63                 #region Events
64
65                 public event XmlNodeChangedEventHandler NodeChanged;
66
67                 public event XmlNodeChangedEventHandler NodeChanging;
68
69                 public event XmlNodeChangedEventHandler NodeInserted;
70
71                 public event XmlNodeChangedEventHandler NodeInserting;
72
73                 public event XmlNodeChangedEventHandler NodeRemoved;
74
75                 public event XmlNodeChangedEventHandler NodeRemoving;
76
77                 #endregion
78
79                 #region Properties
80
81                 public override string BaseURI {
82                         get {
83                                 return baseURI;
84                         }
85                 }
86
87                 // Used to read 'InnerXml's for its descendants at any place.
88                 internal XmlTextReader ReusableReader {
89                         get {
90                                 if(reusableXmlTextReader == null)
91                                         reusableXmlTextReader = new WeakReference (null);
92                                 if(!reusableXmlTextReader.IsAlive) {
93                                         XmlTextReader reader = new XmlTextReader ((TextReader)null);
94                                         reusableXmlTextReader.Target = reader;
95                                 }
96                                 return (XmlTextReader)reusableXmlTextReader.Target;
97                         }
98                 }
99
100                 public XmlElement DocumentElement {
101                         get {
102                                 XmlNode node = FirstChild;
103
104                                 while (node != null) {
105                                         if (node is XmlElement)
106                                                 break;
107                                         node = node.NextSibling;
108                                 }
109
110                                 return node != null ? node as XmlElement : null;
111                         }
112                 }
113
114                 [MonoTODO("It doesn't have internal subset object model.")]
115                 public virtual XmlDocumentType DocumentType {
116                         get {
117                                 foreach(XmlNode n in this.ChildNodes) {
118                                         if(n.NodeType == XmlNodeType.DocumentType)
119                                                 return (XmlDocumentType)n;
120                                 }
121                                 return null;
122                         }
123                 }
124
125                 public XmlImplementation Implementation {
126                         get { return implementation; }
127                 }
128
129                 public override string InnerXml {
130                         get {
131                                 return base.InnerXml;
132                         }
133                         set {   // reason for overriding
134                                 this.LoadXml (value);
135                         }
136                 }
137
138                 public override bool IsReadOnly {
139                         get { return false; }
140                 }
141
142                 internal override XmlLinkedNode LastLinkedChild {
143                         get     {
144                                 return lastLinkedChild;
145                         }
146
147                         set {
148                                 lastLinkedChild = value;
149                         }
150                 }
151                 
152                 public override string LocalName {
153                         get { return "#document"; }
154                 }
155
156                 public override string Name {
157                         get { return "#document"; }
158                 }
159
160                 public XmlNameTable NameTable {
161                         get { return nameTable; }
162                 }
163
164                 public override XmlNodeType NodeType {
165                         get { return XmlNodeType.Document; }
166                 }
167
168                 internal override XPathNodeType XPathNodeType {
169                         get {
170                                 return XPathNodeType.Root;
171                         }
172                 }
173
174                 public override XmlDocument OwnerDocument {
175                         get { return null; }
176                 }
177
178                 public bool PreserveWhitespace {
179                         get { return preserveWhitespace; }
180                         set { preserveWhitespace = value; }
181                 }
182
183                 internal override string XmlLang {
184                         get { return String.Empty; }
185                 }
186
187                 [MonoTODO]
188                 public virtual XmlResolver XmlResolver {
189                         set { throw new NotImplementedException (); }
190                 }
191
192                 internal override XmlSpace XmlSpace {
193                         get {
194                                 return XmlSpace.None;
195                         }
196                 }
197
198                 #endregion
199
200                 #region Methods
201
202                 [MonoTODO("Should BaseURI be cloned?")]
203                 public override XmlNode CloneNode (bool deep)
204                 {
205                         XmlDocument doc = implementation.CreateDocument ();
206                         doc.PreserveWhitespace = PreserveWhitespace;    // required?
207                         if(deep)
208                         {
209                                 foreach(XmlNode n in ChildNodes)
210                                         doc.AppendChild (doc.ImportNode (n, deep));
211                         }
212                         return doc;
213                 }
214
215                 public XmlAttribute CreateAttribute (string name)
216                 {
217                         return CreateAttribute (name, String.Empty);
218                 }
219
220                 public XmlAttribute CreateAttribute (string qualifiedName, string namespaceURI)
221                 {
222                         string prefix;
223                         string localName;
224
225                         ParseName (qualifiedName, out prefix, out localName);
226
227                         return CreateAttribute (prefix, localName, namespaceURI);
228                 }
229
230                 public virtual XmlAttribute CreateAttribute (string prefix, string localName, string namespaceURI)
231                 {
232                         if ((localName == null) || (localName == String.Empty))
233                                 throw new ArgumentException ("The attribute local name cannot be empty.");
234
235                         return new XmlAttribute (prefix, localName, namespaceURI, this);
236                 }
237
238                 public virtual XmlCDataSection CreateCDataSection (string data)
239                 {
240                         return new XmlCDataSection (data, this);
241                 }
242
243                 public virtual XmlComment CreateComment (string data)
244                 {
245                         return new XmlComment (data, this);
246                 }
247
248                 [MonoTODO]
249                 protected internal virtual XmlAttribute CreateDefaultAttribute (string prefix, string localName, string namespaceURI)
250                 {
251                         throw new NotImplementedException ();
252                 }
253
254                 public virtual XmlDocumentFragment CreateDocumentFragment ()
255                 {
256                         return new XmlDocumentFragment (this);
257                 }
258
259                 public virtual XmlDocumentType CreateDocumentType (string name, string publicId,
260                                                                    string systemId, string internalSubset)
261                 {
262                         return new XmlDocumentType (name, publicId, systemId, internalSubset, this);
263                 }
264
265                 public XmlElement CreateElement (string name)
266                 {
267                         return CreateElement (name, String.Empty);
268                 }
269
270                 public XmlElement CreateElement (
271                         string qualifiedName, 
272                         string namespaceURI)
273                 {
274                         string prefix;
275                         string localName;
276
277                         ParseName (qualifiedName, out prefix, out localName);
278                         
279                         return CreateElement (prefix, localName, namespaceURI);
280                 }
281
282                 public virtual XmlElement CreateElement (
283                         string prefix,
284                         string localName,
285                         string namespaceURI)
286                 {
287                         if ((localName == null) || (localName == String.Empty))
288                                 throw new ArgumentException ("The local name for elements or attributes cannot be null or an empty string.");
289                         CheckName (localName);
290                         return new XmlElement (prefix != null ? prefix : String.Empty, localName, namespaceURI != null ? namespaceURI : String.Empty, this);
291                 }
292
293                 public virtual XmlEntityReference CreateEntityReference (string name)
294                 {
295                         return new XmlEntityReference (name, this);
296                 }
297
298                 [MonoTODO]
299                 internal protected virtual XPathNavigator CreateNavigator (XmlNode node)
300                 {
301                         throw new NotImplementedException ();
302                 }
303
304                 public virtual XmlNode CreateNode (
305                         string nodeTypeString,
306                         string name,
307                         string namespaceURI)
308                 {
309                         return CreateNode (GetNodeTypeFromString (nodeTypeString), name, namespaceURI);
310                 }
311
312                 public virtual XmlNode CreateNode (
313                         XmlNodeType type,
314                         string name,
315                         string namespaceURI)
316                 {
317                         string prefix = null;
318                         string localName = name;
319
320                         if ((type == XmlNodeType.Attribute) || (type == XmlNodeType.Element) || (type == XmlNodeType.EntityReference))
321                                 ParseName (name, out prefix, out localName);
322                         
323                         return CreateNode (type, prefix, localName, namespaceURI);
324                 }
325
326                 public virtual XmlNode CreateNode (
327                         XmlNodeType type,
328                         string prefix,
329                         string name,
330                         string namespaceURI)
331                 {
332                         switch (type) {
333                                 case XmlNodeType.Attribute: return CreateAttribute (prefix, name, namespaceURI);
334                                 case XmlNodeType.CDATA: return CreateCDataSection (null);
335                                 case XmlNodeType.Comment: return CreateComment (null);
336                                 case XmlNodeType.Document: return new XmlDocument (); // TODO - test to see which constructor to use, i.e. use existing NameTable or not.
337                                 case XmlNodeType.DocumentFragment: return CreateDocumentFragment ();
338                                 case XmlNodeType.DocumentType: return CreateDocumentType (null, null, null, null);
339                                 case XmlNodeType.Element: return CreateElement (prefix, name, namespaceURI);
340                                 case XmlNodeType.EntityReference: return CreateEntityReference (null);
341                                 case XmlNodeType.ProcessingInstruction: return CreateProcessingInstruction (null, null);
342                                 case XmlNodeType.SignificantWhitespace: return CreateSignificantWhitespace (String.Empty);
343                                 case XmlNodeType.Text: return CreateTextNode (null);
344                                 case XmlNodeType.Whitespace: return CreateWhitespace (String.Empty);
345                                 case XmlNodeType.XmlDeclaration: return CreateXmlDeclaration ("1.0", null, null);
346                                 default: throw new ArgumentOutOfRangeException(String.Format("{0}\nParameter name: {1}",
347                                                          "Specified argument was out of the range of valid values", type.ToString ()));
348                         }
349                 }
350
351                 public virtual XmlProcessingInstruction CreateProcessingInstruction (
352                         string target,
353                         string data)
354                 {
355                         return new XmlProcessingInstruction (target, data, this);
356                 }
357
358                 public virtual XmlSignificantWhitespace CreateSignificantWhitespace (string text)
359                 {
360                         foreach (char c in text)
361                                 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
362                                     throw new ArgumentException ("Invalid whitespace characters.");
363                          
364                         return new XmlSignificantWhitespace (text, this);
365                 }
366
367                 public virtual XmlText CreateTextNode (string text)
368                 {
369                         return new XmlText (text, this);
370                 }
371
372                 public virtual XmlWhitespace CreateWhitespace (string text)
373                 {
374                         foreach (char c in text)
375                                 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
376                                     throw new ArgumentException ("Invalid whitespace characters.");
377                          
378                         return new XmlWhitespace (text, this);
379                 }
380
381                 public virtual XmlDeclaration CreateXmlDeclaration (string version, string encoding,
382                                                                     string standalone)
383                 {
384                         if (version != "1.0")
385                                 throw new ArgumentException ("version string is not correct.");
386
387                         if  ((standalone != null && standalone != String.Empty) && !((standalone == "yes") || (standalone == "no")))
388                                 throw new ArgumentException ("standalone string is not correct.");
389
390                         return new XmlDeclaration (version, encoding, standalone, this);
391                 }
392
393                 [MonoTODO]
394                 public virtual XmlElement GetElementById (string elementId)
395                 {
396                         throw new NotImplementedException ();
397                 }
398
399                 public virtual XmlNodeList GetElementsByTagName (string name)
400                 {
401                         ArrayList nodeArrayList = new ArrayList ();
402                         this.searchNodesRecursively (this, name, nodeArrayList);
403                         return new XmlNodeArrayList (nodeArrayList);
404                 }
405
406                 private void searchNodesRecursively (XmlNode argNode, string argName, 
407                         ArrayList argArrayList)
408                 {
409                         XmlNodeList xmlNodeList = argNode.ChildNodes;
410                         foreach (XmlNode node in xmlNodeList){
411                                 if (node.Name.Equals (argName))
412                                         argArrayList.Add (node);
413                                 else    
414                                         this.searchNodesRecursively (node, argName, argArrayList);
415                         }
416                 }
417
418                 private void searchNodesRecursively (XmlNode argNode, string argName, string argNamespaceURI, 
419                         ArrayList argArrayList)
420                 {
421                         XmlNodeList xmlNodeList = argNode.ChildNodes;
422                         foreach (XmlNode node in xmlNodeList){
423                                 if (node.LocalName.Equals (argName) && node.NamespaceURI.Equals (argNamespaceURI))
424                                         argArrayList.Add (node);
425                                 else    
426                                         this.searchNodesRecursively (node, argName, argNamespaceURI, argArrayList);
427                         }
428                 }
429
430                 public virtual XmlNodeList GetElementsByTagName (string localName, string namespaceURI)
431                 {
432                         ArrayList nodeArrayList = new ArrayList ();
433                         this.searchNodesRecursively (this, localName, namespaceURI, nodeArrayList);
434                         return new XmlNodeArrayList (nodeArrayList);
435                 }
436
437                 private XmlNodeType GetNodeTypeFromString (string nodeTypeString)
438                 {
439                         switch (nodeTypeString) {
440                                 case "attribute": return XmlNodeType.Attribute;
441                                 case "cdatasection": return XmlNodeType.CDATA;
442                                 case "comment": return XmlNodeType.Comment;
443                                 case "document": return XmlNodeType.Document;
444                                 case "documentfragment": return XmlNodeType.DocumentFragment;
445                                 case "documenttype": return XmlNodeType.DocumentType;
446                                 case "element": return XmlNodeType.Element;
447                                 case "entityreference": return XmlNodeType.EntityReference;
448                                 case "processinginstruction": return XmlNodeType.ProcessingInstruction;
449                                 case "significantwhitespace": return XmlNodeType.SignificantWhitespace;
450                                 case "text": return XmlNodeType.Text;
451                                 case "whitespace": return XmlNodeType.Whitespace;
452                                 default:
453                                         throw new ArgumentException(String.Format("The string doesn't represent any node type : {0}.", nodeTypeString));
454                         }
455                 }
456
457                 [MonoTODO("default attributes (of imported doc); Entity; Notation")]
458                 public virtual XmlNode ImportNode (XmlNode node, bool deep)
459                 {
460                         switch(node.NodeType)
461                         {
462                                 case XmlNodeType.Attribute:
463                                         {
464                                                 XmlAttribute src_att = node as XmlAttribute;
465                                                 XmlAttribute dst_att = this.CreateAttribute (src_att.Prefix, src_att.LocalName, src_att.NamespaceURI);
466                                                 dst_att.Value = src_att.Value;  // always explicitly specified (whether source is specified or not)
467                                                 return dst_att;
468                                         }
469
470                                 case XmlNodeType.CDATA:
471                                         return this.CreateCDataSection (node.Value);
472
473                                 case XmlNodeType.Comment:
474                                         return this.CreateComment (node.Value);
475
476                                 case XmlNodeType.Document:
477                                         throw new XmlException ("Document cannot be imported.");
478
479                                 case XmlNodeType.DocumentFragment:
480                                         {
481                                                 XmlDocumentFragment df = this.CreateDocumentFragment ();
482                                                 if(deep)
483                                                 {
484                                                         foreach(XmlNode n in node.ChildNodes)
485                                                         {
486                                                                 df.AppendChild (this.ImportNode (n, deep));
487                                                         }
488                                                 }
489                                                 return df;
490                                         }
491
492                                 case XmlNodeType.DocumentType:
493                                         throw new XmlException ("DocumentType cannot be imported.");
494
495                                 case XmlNodeType.Element:
496                                         {
497                                                 XmlElement src = (XmlElement)node;
498                                                 XmlElement dst = this.CreateElement (src.Prefix, src.LocalName, src.NamespaceURI);
499                                                 foreach(XmlAttribute attr in src.Attributes)
500                                                 {
501                                                         if(attr.Specified)      // copies only specified attributes
502                                                                 dst.SetAttributeNode ((XmlAttribute)this.ImportNode (attr, deep));
503                                                         if(DocumentType != null)
504                                                         {
505                                                                 // TODO: create default attribute values
506                                                         }
507                                                 }
508                                                 if(deep)
509                                                 {
510                                                         foreach(XmlNode n in src.ChildNodes)
511                                                                 dst.AppendChild (this.ImportNode (n, deep));
512                                                 }
513                                                 return dst;
514                                         }
515
516                                 case XmlNodeType.EndElement:
517                                         throw new XmlException ("Illegal ImportNode call for NodeType.EndElement");
518                                 case XmlNodeType.EndEntity:
519                                         throw new XmlException ("Illegal ImportNode call for NodeType.EndEntity");
520
521                                 case XmlNodeType.Entity:
522                                         throw new NotImplementedException ();   // TODO
523
524                                 case XmlNodeType.EntityReference:
525                                         return this.CreateEntityReference (node.Name);
526
527                                 case XmlNodeType.None:
528                                         throw new XmlException ("Illegal ImportNode call for NodeType.None");
529
530                                 case XmlNodeType.Notation:
531                                         throw new NotImplementedException ();   // TODO
532
533                                 case XmlNodeType.ProcessingInstruction:
534                                         XmlProcessingInstruction pi = node as XmlProcessingInstruction;
535                                         return this.CreateProcessingInstruction (pi.Target, pi.Data);
536
537                                 case XmlNodeType.SignificantWhitespace:
538                                         return this.CreateSignificantWhitespace (node.Value);
539
540                                 case XmlNodeType.Text:
541                                         return this.CreateTextNode (node.Value);
542
543                                 case XmlNodeType.Whitespace:
544                                         return this.CreateWhitespace (node.Value);
545
546                                 case XmlNodeType.XmlDeclaration:
547                                         XmlDeclaration srcDecl = node as XmlDeclaration;
548                                         return this.CreateXmlDeclaration (srcDecl.Version, srcDecl.Encoding, srcDecl.Standalone);
549
550                                 default:
551                                         throw new NotImplementedException ();
552                         }
553                 }
554
555                 public virtual void Load (Stream inStream)
556                 {
557                         Load (new XmlTextReader (new XmlStreamReader (inStream)));
558                 }
559
560                 public virtual void Load (string filename)
561                 {
562                         Load (new XmlTextReader (filename));
563                 }
564
565                 public virtual void Load (TextReader txtReader)
566                 {
567                         Load (new XmlTextReader (txtReader));
568                 }
569
570                 public virtual void Load (XmlReader xmlReader)
571                 {
572                         // Reset our document
573                         // For now this just means removing all our children but later this
574                         // may turn out o need to call a private method that resets other things
575                         // like properties we have, etc.
576                         RemoveAll ();
577
578                         this.baseURI = xmlReader.BaseURI;
579                         // create all contents with use of ReadNode()
580                         do {
581                                 XmlNode n = ReadNode (xmlReader);
582                                 if(n == null) break;
583                                 AppendChild (n);
584                         } while (true);
585                         xmlReader.Close ();
586                 }
587
588                 public virtual void LoadXml (string xml)
589                 {
590                         XmlReader xmlReader = new XmlTextReader (new StringReader (xml));
591                         Load (xmlReader);
592                 }
593
594                 internal void onNodeChanged (XmlNode node, XmlNode Parent)
595                 {
596                         if (NodeChanged != null)
597                                 NodeChanged (node, new XmlNodeChangedEventArgs
598                                         (XmlNodeChangedAction.Change,
599                                         node, Parent, Parent));
600                 }
601
602                 internal void onNodeChanging(XmlNode node, XmlNode Parent)
603                 {
604                         if (NodeChanging != null)
605                                 NodeChanging (node, new XmlNodeChangedEventArgs
606                                         (XmlNodeChangedAction.Change,
607                                         node, Parent, Parent));
608                 }
609
610                 internal void onNodeInserted (XmlNode node, XmlNode newParent)
611                 {
612                         if (NodeInserted != null)
613                                 NodeInserted (node, new XmlNodeChangedEventArgs
614                                         (XmlNodeChangedAction.Insert,
615                                         node, null, newParent));
616                 }
617
618                 internal void onNodeInserting (XmlNode node, XmlNode newParent)
619                 {
620                         if (NodeInserting != null)
621                                 NodeInserting (node, new XmlNodeChangedEventArgs
622                                         (XmlNodeChangedAction.Insert,
623                                         node, null, newParent));
624                 }
625
626                 internal void onNodeRemoved (XmlNode node, XmlNode oldParent)
627                 {
628                         if (NodeRemoved != null)
629                                 NodeRemoved (node, new XmlNodeChangedEventArgs
630                                         (XmlNodeChangedAction.Remove,
631                                         node, oldParent, null));
632                 }
633
634                 internal void onNodeRemoving (XmlNode node, XmlNode oldParent)
635                 {
636                         if (NodeRemoving != null)
637                                 NodeRemoving (node, new XmlNodeChangedEventArgs
638                                         (XmlNodeChangedAction.Remove,
639                                         node, oldParent, null));
640                 }
641
642                 private void ParseName (string name, out string prefix, out string localName)
643                 {
644                         int indexOfColon = name.IndexOf (':');
645                         
646                         if (indexOfColon != -1) {
647                                 prefix = name.Substring (0, indexOfColon);
648                                 localName = name.Substring (indexOfColon + 1);
649                         } else {
650                                 prefix = "";
651                                 localName = name;
652                         }
653                 }
654
655                 // Checks that Element's name is valid
656                 private void CheckName (String name)
657                 {
658                         // TODO: others validations?
659                         if (name.IndexOf (" ") >= 0)
660                                 throw new XmlException ("The ' ' characted cannot be included in a name");
661                 }
662
663                 // Reads XmlReader and creates Attribute Node.
664                 private XmlAttribute ReadAttributeNode(XmlReader reader)
665                 {
666                         if(reader.NodeType == XmlNodeType.Element)
667                                 reader.MoveToFirstAttribute ();
668                         else if(reader.NodeType != XmlNodeType.Attribute)
669                                 throw new InvalidOperationException (MakeReaderErrorMessage ("bad position to read attribute.", reader));
670                         XmlAttribute attribute = CreateAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
671                         ReadAttributeNodeValue (reader, attribute);
672                         return attribute;
673                 }
674
675                 // Reads attribute from XmlReader and then creates attribute value children. XmlAttribute also uses this.
676                 internal void ReadAttributeNodeValue(XmlReader reader, XmlAttribute attribute)
677                 {
678                         while(reader.ReadAttributeValue ()) {
679                                 if(reader.NodeType == XmlNodeType.EntityReference)
680                                         // FIXME: if DocumentType is available, then try to resolve it.
681                                         attribute.AppendChild (CreateEntityReference (reader.Name));
682                                 // FIXME: else if(NodeType == EndEntity) -- reset BaseURI and so on -- ;
683                                 else
684                                         // (IMHO) Children of Attribute is likely restricted to Text and EntityReference.
685                                         attribute.AppendChild (CreateTextNode (reader.Value));
686                         }
687                 }
688
689                 [MonoTODO ("Child of entity is not simple Value string;Get prefix of NotationDecl")]
690                 public virtual XmlNode ReadNode(XmlReader reader)
691                 {
692                         XmlNode resultNode = null;
693                         XmlNode newNode = null;
694                         XmlNode currentNode = null;
695
696                         if (reader.ReadState == ReadState.Initial)
697                                 reader.Read ();
698
699                         int startDepth = reader.Depth;
700                         bool ignoredWhitespace;
701                         bool reachedEOF = false;
702
703                         do {
704                                 ignoredWhitespace = false;
705                                 if (reader.NodeType == XmlNodeType.None)
706                                         if (reachedEOF)
707                                                 throw new Exception ("XML Reader reached to end while reading node.");
708                                         else
709                                                 reachedEOF = true;
710                                 switch (reader.NodeType) {
711
712                                 case XmlNodeType.Attribute:
713                                         newNode = ReadAttributeNode (reader);
714                                         break;
715
716                                 case XmlNodeType.CDATA:
717                                         newNode = CreateCDataSection (reader.Value);
718                                         if(currentNode != null)
719                                                 currentNode.AppendChild (newNode);
720                                         break;
721
722                                 case XmlNodeType.Comment:
723                                         newNode = CreateComment (reader.Value);
724                                         if(currentNode != null)
725                                                 currentNode.AppendChild (newNode);
726                                         break;
727
728                                 case XmlNodeType.Element:
729                                         XmlElement element = CreateElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
730                                         element.IsEmpty = reader.IsEmptyElement;
731                                         if(currentNode != null)
732                                                 currentNode.AppendChild (element);
733                                         else
734                                                 resultNode = element;
735
736                                         // set the element's attributes.
737                                         while (reader.MoveToNextAttribute ()) {
738                                                 element.SetAttributeNode (ReadAttributeNode (reader));
739                                         }
740
741                                         reader.MoveToElement ();
742
743                                         if (!reader.IsEmptyElement)
744                                                 currentNode = element;
745
746                                         break;
747
748                                 case XmlNodeType.EndElement:
749                                         if (currentNode == null)
750                                                 throw new XmlException ("Unexpected end element.");
751                                         else if (currentNode.Name != reader.Name)
752                                                 throw new XmlException (MakeReaderErrorMessage (String.Format ("mismatch end tag. Expected {0} but found {1}", currentNode.Name, reader.Name), reader));
753                                         currentNode = currentNode.ParentNode;
754                                         break;
755
756                                 case XmlNodeType.EndEntity:
757                                         break;  // no operation
758
759                                 case XmlNodeType.ProcessingInstruction:
760                                         newNode = CreateProcessingInstruction (reader.Name, reader.Value);
761                                         if(currentNode != null)
762                                                 currentNode.AppendChild (newNode);
763                                         break;
764
765                                 case XmlNodeType.Text:
766                                         newNode = CreateTextNode (reader.Value);
767                                         if(currentNode != null)
768                                                 currentNode.AppendChild (newNode);
769                                         break;
770
771                                 case XmlNodeType.XmlDeclaration:
772                                         // empty strings are dummy, then gives over setting value contents to setter.
773                                         newNode = CreateXmlDeclaration ("1.0" , String.Empty, String.Empty);
774                                         ((XmlDeclaration)newNode).Value = reader.Value;
775                                         if(currentNode != null)
776                                                 throw new XmlException (MakeReaderErrorMessage ("XmlDeclaration at invalid position.", reader));
777                                         break;
778
779                                 case XmlNodeType.DocumentType:
780                                         // hack ;-)
781                                         XmlTextReader xtReader = reader as XmlTextReader;
782                                         if(xtReader == null) {
783                                                 // XmlTextReader doesn't allow such creation that starts reading from Doctype.
784                                         /*
785                                                 xtReader = new XmlTextReader (reader.ReadOuterXml (),
786                                                         XmlNodeType.DocumentType,
787                                                         new XmlParserContext (NameTable, ConstructNamespaceManager(), XmlLang, XmlSpace));
788                                                 xtReader.Read ();
789                                         */
790                                                 newNode = CreateDocumentType (reader.Name, reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
791                                         } else {
792                                                 newNode = ReadDoctypeNode (xtReader);
793                                         }
794                                         if(currentNode != null)
795                                                 throw new XmlException (this.MakeReaderErrorMessage ("XmlDocumentType at invalid position.", reader));
796                                         break;
797
798                                 case XmlNodeType.EntityReference:
799                                         newNode = CreateEntityReference (reader.Name);
800                                         if(currentNode != null)
801                                                 currentNode.AppendChild (newNode);
802                                         break;
803
804                                 case XmlNodeType.SignificantWhitespace:
805                                         newNode = CreateSignificantWhitespace (reader.Value);
806                                         if(currentNode != null)
807                                                 currentNode.AppendChild (newNode);
808                                         break;
809
810                                 case XmlNodeType.Whitespace:
811                                         if(PreserveWhitespace) {
812                                                 newNode = CreateWhitespace (reader.Value);
813                                                 if(currentNode != null)
814                                                         currentNode.AppendChild (newNode);
815                                         }
816                                         else
817                                                 ignoredWhitespace = true;
818                                         break;
819                                 }
820                                 if (!reader.Read ())
821                                         break;
822                         } while (ignoredWhitespace || reader.Depth > startDepth ||
823                                 (reader.Depth == startDepth && reader.NodeType == XmlNodeType.EndElement));
824                         if (startDepth != reader.Depth && reader.EOF)
825                                 throw new XmlException ("Unexpected end of xml reader.");
826                         return resultNode != null ? resultNode : newNode;
827                 }
828
829                 private string MakeReaderErrorMessage (string message, XmlReader reader)
830                 {
831                         IXmlLineInfo li = reader as IXmlLineInfo;
832                         if (li != null)
833                                 return String.Format ("{0} Line number = {1}, Inline position = {2}.", li.LineNumber, li.LinePosition);
834                         else
835                                 return message;
836                 }
837
838                 private XmlDocumentType ReadDoctypeNode (XmlTextReader xtReader)
839                 {
840                         XmlDocumentType doctype = CreateDocumentType (xtReader.Name,
841                                 xtReader.GetAttribute ("PUBLIC"),
842                                 xtReader.GetAttribute ("SYSTEM"),
843                                 xtReader.Value);
844                         DTDObjectModel subset = xtReader.currentSubset;
845                         foreach (DTDEntityDeclaration decl in subset.EntityDecls.Values) {
846                                 XmlNode n = new XmlEntity (decl.Name, decl.NotationName,
847                                         decl.PublicId, decl.SystemId, this);
848                                 // FIXME: Value is more complex, similar to Attribute.
849                                 n.insertBeforeIntern (this.CreateTextNode (decl.EntityValue), null);
850                                 doctype.entities.Nodes.Add (n);
851                         }
852                         foreach (DTDNotationDeclaration decl in subset.NotationDecls.Values) {
853                                 XmlNode n = new XmlNotation (decl.LocalName, decl.Prefix,
854                                         decl.PublicId, decl.SystemId, this);
855                                 doctype.notations.Nodes.Add (n);
856                         }
857                         foreach (DTDElementDeclaration decl in subset.ElementDecls.Values) {
858                                 doctype.elementDecls.Add (decl.Name, decl.Clone ());
859                         }
860                         foreach (DTDAttListDeclaration decl in subset.AttListDecls.Values) {
861                                 doctype.attListDecls.Add (decl.Name, decl.Clone ());
862                         }
863                         return doctype;
864                 }
865
866                 public virtual void Save(Stream outStream)
867                 {
868                         XmlTextWriter xmlWriter = new XmlTextWriter (outStream, Encoding.UTF8);
869                         xmlWriter.Formatting = Formatting.Indented;
870                         WriteContentTo (xmlWriter);
871                         xmlWriter.Close ();
872                 }
873
874                 public virtual void Save (string filename)
875                 {
876                         XmlTextWriter xmlWriter = new XmlTextWriter (filename, Encoding.UTF8);
877                         xmlWriter.Formatting = Formatting.Indented;
878                         WriteContentTo (xmlWriter);
879                         xmlWriter.Close ();
880                 }
881
882                 [MonoTODO]
883                 public virtual void Save (TextWriter writer)
884                 {
885                         XmlTextWriter xmlWriter = new XmlTextWriter (writer);
886                         xmlWriter.Formatting = Formatting.Indented;
887                         WriteContentTo (xmlWriter);
888                         xmlWriter.Flush ();
889                 }
890
891                 public virtual void Save (XmlWriter xmlWriter)
892                 {
893                         //
894                         // This should preserve white space if PreserveWhiteSpace is true
895                         //
896                         WriteContentTo (xmlWriter);
897                         xmlWriter.Flush ();
898                 }
899
900                 public override void WriteContentTo (XmlWriter w)
901                 {
902                         foreach(XmlNode childNode in ChildNodes) {
903                                 childNode.WriteTo (w);
904                         }
905                 }
906
907                 public override void WriteTo (XmlWriter w)
908                 {
909                         WriteContentTo (w);
910                 }
911
912                 private void AddDefaultNameTableKeys ()
913                 {
914                         // The following keys are default of MS .NET Framework
915                         nameTable.Add ("#text");
916                         nameTable.Add ("xml");
917                         nameTable.Add ("xmlns");
918                         nameTable.Add ("#entity");
919                         nameTable.Add ("#document-fragment");
920                         nameTable.Add ("#comment");
921                         nameTable.Add ("space");
922                         nameTable.Add ("id");
923                         nameTable.Add ("#whitespace");
924                         nameTable.Add ("http://www.w3.org/2000/xmlns/");
925                         nameTable.Add ("#cdata-section");
926                         nameTable.Add ("lang");
927                         nameTable.Add ("#document");
928                         nameTable.Add ("#significant-whitespace");
929                 }
930                 #endregion
931         }
932 }