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