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