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