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