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