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