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