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