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