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