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