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