2002-05-26 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocument.cs
1 //
2 // System.Xml.XmlDocument
3 //
4 // Authors:
5 //   Daniel Weber (daniel-weber@austin.rr.com)
6 //   Kral Ferch <kral_ferch@hotmail.com>
7 //   Jason Diamond <jason@injektilo.org>
8 //   Miguel de Icaza (miguel@ximian.com)
9 //
10 // (C) 2001 Daniel Weber
11 // (C) 2002 Kral Ferch, Jason Diamond, Miguel de Icaza
12 //
13
14 using System;
15 using System.IO;
16 using System.Text;
17 using System.Xml.XPath;
18 using System.Diagnostics;
19
20 namespace System.Xml
21 {
22         public class XmlDocument : XmlNode
23         {
24                 #region Fields
25
26                 XmlLinkedNode lastLinkedChild;
27                 XmlNameTable nameTable;
28
29                 #endregion
30
31                 #region Constructors
32
33                 public XmlDocument () : base (null) { }
34
35                 [MonoTODO]
36                 protected internal XmlDocument (XmlImplementation imp) : base (null)
37                 {
38                         throw new NotImplementedException ();
39                 }
40
41                 public XmlDocument (XmlNameTable nt) : base (null)
42                 {
43                         nameTable = nt;
44                 }
45
46                 #endregion
47
48                 #region Events
49
50                 public event XmlNodeChangedEventHandler NodeChanged;
51
52                 public event XmlNodeChangedEventHandler NodeChanging;
53
54                 public event XmlNodeChangedEventHandler NodeInserted;
55
56                 public event XmlNodeChangedEventHandler NodeInserting;
57
58                 public event XmlNodeChangedEventHandler NodeRemoved;
59
60                 public event XmlNodeChangedEventHandler NodeRemoving;
61
62                 #endregion
63
64                 #region Properties
65
66                 [MonoTODO]
67                 public override string BaseURI {
68                         get { throw new NotImplementedException(); }
69                 }
70
71                 public XmlElement DocumentElement {
72                         get {
73                                 XmlNode node = FirstChild;
74
75                                 while (node != null) {
76                                         if (node is XmlElement)
77                                                 break;
78                                         node = node.NextSibling;
79                                 }
80
81                                 return node != null ? node as XmlElement : null;
82                         }
83                 }
84
85                 [MonoTODO]
86                 public virtual XmlDocumentType DocumentType {
87                         get { throw new NotImplementedException(); }
88                 }
89
90                 [MonoTODO]
91                 public XmlImplementation Implementation {
92                         get { throw new NotImplementedException(); }
93                 }
94
95                 [MonoTODO ("Setter.")]
96                 public override string InnerXml {
97                         get {
98                                 // Not sure why this is an override.  Passing through for now.
99                                 return base.InnerXml;
100                         }
101                         set { throw new NotImplementedException(); }
102                 }
103
104                 public override bool IsReadOnly {
105                         get { return false; }
106                 }
107
108                 internal override XmlLinkedNode LastLinkedChild {
109                         get     {
110                                 return lastLinkedChild;
111                         }
112
113                         set {
114                                 lastLinkedChild = value;
115                         }
116                 }
117                 
118                 public override string LocalName {
119                         get { return "#document"; }
120                 }
121
122                 public override string Name {
123                         get { return "#document"; }
124                 }
125
126                 public XmlNameTable NameTable {
127                         get { return nameTable; }
128                 }
129
130                 public override XmlNodeType NodeType {
131                         get { return XmlNodeType.Document; }
132                 }
133
134                 public override XmlDocument OwnerDocument {
135                         get { return null; }
136                 }
137
138                 [MonoTODO]
139                 public bool PreserveWhitespace {
140                         get { throw new NotImplementedException(); }
141                         set { throw new NotImplementedException(); }
142                 }
143
144                 [MonoTODO]
145                 public virtual XmlResolver XmlResolver {
146                         set { throw new NotImplementedException(); }
147                 }
148
149                 #endregion
150
151                 #region Methods
152
153                 [MonoTODO]
154                 public override XmlNode CloneNode (bool deep)
155                 {
156                         throw new NotImplementedException ();
157                 }
158
159                 public XmlAttribute CreateAttribute (string name)
160                 {
161                         return CreateAttribute (name, String.Empty);
162                 }
163
164                 public XmlAttribute CreateAttribute (string qualifiedName, string namespaceURI)
165                 {
166                         string prefix;
167                         string localName;
168
169                         ParseName (qualifiedName, out prefix, out localName);
170
171                         return CreateAttribute (prefix, localName, namespaceURI);
172                 }
173
174                 public virtual XmlAttribute CreateAttribute (string prefix, string localName, string namespaceURI)
175                 {
176                         if ((localName == null) || (localName == String.Empty))
177                                 throw new ArgumentException ("The attribute local name cannot be empty.");
178
179                         return new XmlAttribute (prefix, localName, namespaceURI, this);
180                 }
181
182                 public virtual XmlCDataSection CreateCDataSection (string data)
183                 {
184                         return new XmlCDataSection (data, this);
185                 }
186
187                 public virtual XmlComment CreateComment (string data)
188                 {
189                         return new XmlComment(data, this);
190                 }
191
192                 [MonoTODO]
193                 protected internal virtual XmlAttribute CreateDefaultAttribute (string prefix, string localName, string namespaceURI)
194                 {
195                         throw new NotImplementedException ();
196                 }
197
198                 [MonoTODO]
199                 public virtual XmlDocumentFragment CreateDocumentFragment ()
200                 {
201                         throw new NotImplementedException ();
202                 }
203
204                 public virtual XmlDocumentType CreateDocumentType (string name, string publicId,
205                                                                    string systemId, string internalSubset)
206                 {
207                         return new XmlDocumentType (name, publicId, systemId, internalSubset, this);
208                 }
209
210                 public XmlElement CreateElement (string name)
211                 {
212                         return CreateElement (name, String.Empty);
213                 }
214
215                 public XmlElement CreateElement (
216                         string qualifiedName, 
217                         string namespaceURI)
218                 {
219                         string prefix;
220                         string localName;
221
222                         ParseName (qualifiedName, out prefix, out localName);
223
224                         return CreateElement (prefix, localName, namespaceURI);
225                 }
226
227                 public virtual XmlElement CreateElement (
228                         string prefix,
229                         string localName,
230                         string namespaceURI)
231                 {
232                         if ((localName == null) || (localName == String.Empty))
233                                 throw new ArgumentException ("The local name for elements or attributes cannot be null or an empty string.");
234
235                         return new XmlElement (prefix, localName, namespaceURI, this);
236                 }
237
238                 [MonoTODO]
239                 public virtual XmlEntityReference CreateEntityReference (string name)
240                 {
241                         throw new NotImplementedException ();
242                 }
243
244                 [MonoTODO]
245                 protected internal virtual XPathNavigator CreateNavigator (XmlNode node)
246                 {
247                         throw new NotImplementedException ();
248                 }
249
250                 public virtual XmlNode CreateNode (
251                         string nodeTypeString,
252                         string name,
253                         string namespaceURI)
254                 {
255                         return CreateNode (GetNodeTypeFromString (nodeTypeString), name, namespaceURI);
256                 }
257
258                 public virtual XmlNode CreateNode (
259                         XmlNodeType type,
260                         string name,
261                         string namespaceURI)
262                 {
263                         string prefix = null;
264                         string localName = name;
265
266                         if ((type == XmlNodeType.Attribute) || (type == XmlNodeType.Element) || (type == XmlNodeType.EntityReference))
267                                 ParseName (name, out prefix, out localName);
268                         
269                         return CreateNode (type, prefix, localName, namespaceURI);
270                 }
271
272                 public virtual XmlNode CreateNode (
273                         XmlNodeType type,
274                         string prefix,
275                         string name,
276                         string namespaceURI)
277                 {
278                         switch (type) {
279                                 case XmlNodeType.Attribute: return CreateAttribute (prefix, name, namespaceURI);
280                                 case XmlNodeType.CDATA: return CreateCDataSection (null);
281                                 case XmlNodeType.Comment: return CreateComment (null);
282                                 case XmlNodeType.Document: return new XmlDocument (); // TODO - test to see which constructor to use, i.e. use existing NameTable or not.
283                                 case XmlNodeType.DocumentFragment: return CreateDocumentFragment ();
284                                 case XmlNodeType.DocumentType: return CreateDocumentType (null, null, null, null);
285                                 case XmlNodeType.Element: return CreateElement (prefix, name, namespaceURI);
286                                 case XmlNodeType.EntityReference: return CreateEntityReference (null);
287                                 case XmlNodeType.ProcessingInstruction: return CreateProcessingInstruction (null, null);
288                                 case XmlNodeType.SignificantWhitespace: return CreateSignificantWhitespace (String.Empty);
289                                 case XmlNodeType.Text: return CreateTextNode (null);
290                                 case XmlNodeType.Whitespace: return CreateWhitespace (String.Empty);
291                                 case XmlNodeType.XmlDeclaration: return CreateXmlDeclaration ("1.0", null, null);
292                                 default: throw new ArgumentOutOfRangeException(String.Format("{0}\nParameter name: {1}",
293                                                          "Specified argument was out of the range of valid values", type.ToString ()));
294                         }
295                 }
296
297                 public virtual XmlProcessingInstruction CreateProcessingInstruction (
298                         string target,
299                         string data)
300                 {
301                         return new XmlProcessingInstruction (target, data, this);
302                 }
303
304                 public virtual XmlSignificantWhitespace CreateSignificantWhitespace (string text)
305                 {
306                         foreach (char c in text)
307                                 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
308                                     throw new ArgumentException ("Invalid whitespace characters.");
309                          
310                         return new XmlSignificantWhitespace (text, this);
311                 }
312
313                 public virtual XmlText CreateTextNode (string text)
314                 {
315                         return new XmlText (text, this);
316                 }
317
318                 public virtual XmlWhitespace CreateWhitespace (string text)
319                 {
320                         foreach (char c in text)
321                                 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
322                                     throw new ArgumentException ("Invalid whitespace characters.");
323                          
324                         return new XmlWhitespace (text, this);
325                 }
326
327                 public virtual XmlDeclaration CreateXmlDeclaration (string version, string encoding,
328                                                                     string standalone)
329                 {
330                         if (version != "1.0")
331                                 throw new ArgumentException ("version string is not correct.");
332
333                         if  ((standalone != null) && !((standalone == "yes") || (standalone == "no")))
334                                 throw new ArgumentException ("standalone string is not correct.");
335                         
336                         return new XmlDeclaration (version, encoding, standalone, this);
337                 }
338
339                 [MonoTODO]
340                 public virtual XmlElement GetElementById (string elementId)
341                 {
342                         throw new NotImplementedException ();
343                 }
344
345                 [MonoTODO]
346                 public virtual XmlNodeList GetElementsByTagName (string name)
347                 {
348                         throw new NotImplementedException ();
349                 }
350
351                 [MonoTODO]
352                 public virtual XmlNodeList GetElementsByTagName (string localName, string namespaceURI)
353                 {
354                         throw new NotImplementedException();
355                 }
356
357                 private XmlNodeType GetNodeTypeFromString (string nodeTypeString)
358                 {
359                         switch (nodeTypeString) {
360                                 case "attribute": return XmlNodeType.Attribute;
361                                 case "cdatasection": return XmlNodeType.CDATA;
362                                 case "comment": return XmlNodeType.Comment;
363                                 case "document": return XmlNodeType.Document;
364                                 case "documentfragment": return XmlNodeType.DocumentFragment;
365                                 case "documenttype": return XmlNodeType.DocumentType;
366                                 case "element": return XmlNodeType.Element;
367                                 case "entityreference": return XmlNodeType.EntityReference;
368                                 case "processinginstruction": return XmlNodeType.ProcessingInstruction;
369                                 case "significantwhitespace": return XmlNodeType.SignificantWhitespace;
370                                 case "text": return XmlNodeType.Text;
371                                 case "whitespace": return XmlNodeType.Whitespace;
372                                 default:
373                                         throw new ArgumentException(String.Format("The string doesn't represent any node type : {0}.", nodeTypeString));
374                         }
375                 }
376
377                 [MonoTODO]
378                 public virtual XmlNode ImportNode (XmlNode node, bool deep)
379                 {
380                         throw new NotImplementedException ();
381                 }
382
383                 [MonoTODO]
384                 public virtual void Load (Stream inStream)
385                 {
386                         throw new NotImplementedException ();
387                 }
388
389                 public virtual void Load (string filename)
390                 {
391                         XmlReader xmlReader = new XmlTextReader (new StreamReader (filename));
392                         Load (xmlReader);
393                 }
394
395                 [MonoTODO]
396                 public virtual void Load (TextReader txtReader)
397                 {
398                         throw new NotImplementedException ();
399                 }
400
401                 public virtual void Load (XmlReader xmlReader)
402                 {
403                         // Reset our document
404                         // For now this just means removing all our children but later this
405                         // may turn out o need to call a private method that resets other things
406                         // like properties we have, etc.
407                         RemoveAll ();
408
409                         XmlNode currentNode = this;
410                         XmlNode newNode;
411
412                         while (xmlReader.Read ()) 
413                         {
414                                 switch (xmlReader.NodeType) {
415
416                                 case XmlNodeType.CDATA:
417                                         newNode = CreateCDataSection(xmlReader.Value);
418                                         currentNode.AppendChild (newNode);
419                                         break;
420
421                                 case XmlNodeType.Comment:
422                                         newNode = CreateComment (xmlReader.Value);
423                                         currentNode.AppendChild (newNode);
424                                         break;
425
426                                 case XmlNodeType.Element:
427                                         XmlElement element = CreateElement (xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI);
428                                         currentNode.AppendChild (element);
429
430                                         // set the element's attributes.
431                                         while (xmlReader.MoveToNextAttribute ())
432                                                 element.SetAttribute (xmlReader.Name, xmlReader.Value);
433
434                                         xmlReader.MoveToElement ();
435
436                                         // if this element isn't empty, push it onto our "stack".
437                                         if (!xmlReader.IsEmptyElement)
438                                                 currentNode = element;
439
440                                         break;
441
442                                 case XmlNodeType.EndElement:
443                                         currentNode = currentNode.ParentNode;
444                                         break;
445
446                                 case XmlNodeType.ProcessingInstruction:
447                                         newNode = CreateProcessingInstruction (xmlReader.Name, xmlReader.Value);
448                                         currentNode.AppendChild (newNode);
449                                         break;
450
451                                 case XmlNodeType.Text:
452                                         newNode = CreateTextNode (xmlReader.Value);
453                                         currentNode.AppendChild (newNode);
454                                         break;
455                                 }
456                         }
457                 }
458
459                 public virtual void LoadXml (string xml)
460                 {
461                         XmlReader xmlReader = new XmlTextReader (new StringReader (xml));
462                         Load (xmlReader);
463                 }
464
465                 internal void onNodeChanged (XmlNode node, XmlNode Parent)
466                 {
467                         if (NodeChanged != null)
468                                 NodeInserted (node, new XmlNodeChangedEventArgs
469                                         (XmlNodeChangedAction.Change,
470                                         node, Parent, Parent));
471                 }
472
473                 internal void onNodeChanging(XmlNode node, XmlNode Parent)
474                 {
475                         if (NodeInserting != null)
476                                 NodeChanging (node, new XmlNodeChangedEventArgs
477                                         (XmlNodeChangedAction.Change,
478                                         node, Parent, Parent));
479                 }
480
481                 internal void onNodeInserted (XmlNode node, XmlNode newParent)
482                 {
483                         if (NodeInserted != null)
484                                 NodeInserted (node, new XmlNodeChangedEventArgs
485                                         (XmlNodeChangedAction.Insert,
486                                         node, null, newParent));
487                 }
488
489                 internal void onNodeInserting (XmlNode node, XmlNode newParent)
490                 {
491                         if (NodeInserting != null)
492                                 NodeInserting (node, new XmlNodeChangedEventArgs
493                                         (XmlNodeChangedAction.Insert,
494                                         node, null, newParent));
495                 }
496
497                 internal void onNodeRemoved (XmlNode node, XmlNode oldParent)
498                 {
499                         if (NodeRemoved != null)
500                                 NodeRemoved (node, new XmlNodeChangedEventArgs
501                                         (XmlNodeChangedAction.Remove,
502                                         node, oldParent, null));
503                 }
504
505                 internal void onNodeRemoving (XmlNode node, XmlNode oldParent)
506                 {
507                         if (NodeRemoving != null)
508                                 NodeRemoving (node, new XmlNodeChangedEventArgs
509                                         (XmlNodeChangedAction.Remove,
510                                         node, oldParent, null));
511                 }
512
513                 private void ParseName (string name, out string prefix, out string localName)
514                 {
515                         int indexOfColon = name.IndexOf (':');
516                         
517                         if (indexOfColon != -1) {
518                                 prefix = name.Substring (0, indexOfColon);
519                                 localName = name.Substring (indexOfColon + 1);
520                         } else {
521                                 prefix = "";
522                                 localName = name;
523                         }
524                 }
525
526                 [MonoTODO]
527                 public virtual XmlNode ReadNode(XmlReader reader)
528                 {
529                         throw new NotImplementedException ();
530                 }
531
532                 [MonoTODO ("Verify what encoding is used by default;  Should use PreserveWhiteSpace")]
533                 public virtual void Save(Stream outStream)
534                 {
535                         XmlTextWriter xmlWriter = new XmlTextWriter (outStream, Encoding.UTF8);
536                         WriteContentTo (xmlWriter);
537                         xmlWriter.Close ();
538                 }
539
540                 [MonoTODO ("Verify what encoding is used by default; Should use PreseveWhiteSpace")]
541                 public virtual void Save (string filename)
542                 {
543                         XmlTextWriter xmlWriter = new XmlTextWriter (filename, Encoding.UTF8);
544                         WriteContentTo (xmlWriter);
545                         xmlWriter.Close ();
546                 }
547
548                 [MonoTODO]
549                 public virtual void Save (TextWriter writer)
550                 {
551                         XmlTextWriter xmlWriter = new XmlTextWriter (writer);
552                         WriteContentTo (xmlWriter);
553                         xmlWriter.Flush ();
554                 }
555
556                 [MonoTODO ("Should preserve white space if PreserveWhisspace is set")]
557                 public virtual void Save (XmlWriter xmlWriter)
558                 {
559                         //
560                         // This should preserve white space if PreserveWhiteSpace is true
561                         //
562                         WriteContentTo (xmlWriter);
563                         xmlWriter.Flush ();
564                 }
565
566                 public override void WriteContentTo (XmlWriter w)
567                 {
568                         foreach(XmlNode childNode in ChildNodes)
569                                 childNode.WriteTo(w);
570                 }
571
572                 public override void WriteTo (XmlWriter w)
573                 {
574                         WriteContentTo(w);
575                 }
576
577                 #endregion
578         }
579 }