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