2 // System.Xml.XmlDocument
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)
11 // (C) 2001 Daniel Weber
12 // (C) 2002 Kral Ferch, Jason Diamond, Miguel de Icaza, Duncan Mak
18 using System.Xml.XPath;
19 using System.Diagnostics;
20 using System.Collections;
24 public class XmlDocument : XmlNode
28 XmlLinkedNode lastLinkedChild;
29 XmlNameTable nameTable;
30 string baseURI = String.Empty;
31 XmlImplementation implementation;
32 bool preserveWhitespace = true; // Its true initial value is false.
38 public XmlDocument () : base (null)
40 implementation = new XmlImplementation();
41 nameTable = implementation.internalNameTable;
42 AddDefaultNameTableKeys();
45 protected internal XmlDocument (XmlImplementation imp) : base (null)
48 nameTable = imp.internalNameTable;
49 AddDefaultNameTableKeys();
52 public XmlDocument (XmlNameTable nt) : base (null)
54 implementation = new XmlImplementation();
55 implementation.internalNameTable = nt;
57 AddDefaultNameTableKeys();
64 public event XmlNodeChangedEventHandler NodeChanged;
66 public event XmlNodeChangedEventHandler NodeChanging;
68 public event XmlNodeChangedEventHandler NodeInserted;
70 public event XmlNodeChangedEventHandler NodeInserting;
72 public event XmlNodeChangedEventHandler NodeRemoved;
74 public event XmlNodeChangedEventHandler NodeRemoving;
80 public override string BaseURI {
86 public XmlElement DocumentElement {
88 XmlNode node = FirstChild;
90 while (node != null) {
91 if (node is XmlElement)
93 node = node.NextSibling;
96 return node != null ? node as XmlElement : null;
100 [MonoTODO("It doesn't have internal subset object model.")]
101 public virtual XmlDocumentType DocumentType {
103 foreach(XmlNode n in this.ChildNodes) {
104 if(n.NodeType == XmlNodeType.DocumentType)
105 return (XmlDocumentType)n;
111 public XmlImplementation Implementation {
112 get { return implementation; }
115 public override string InnerXml {
117 return base.InnerXml;
119 set { // reason for overriding
124 public override bool IsReadOnly {
125 get { return false; }
128 internal override XmlLinkedNode LastLinkedChild {
130 return lastLinkedChild;
134 lastLinkedChild = value;
138 public override string LocalName {
139 get { return "#document"; }
142 public override string Name {
143 get { return "#document"; }
146 public XmlNameTable NameTable {
147 get { return nameTable; }
150 public override XmlNodeType NodeType {
151 get { return XmlNodeType.Document; }
154 internal override XPathNodeType XPathNodeType {
156 return XPathNodeType.Root;
160 public override XmlDocument OwnerDocument {
164 [MonoTODO("wait for getting 'xml:space' status for each node")]
165 public bool PreserveWhitespace {
166 get { return preserveWhitespace; }
167 set { preserveWhitespace = value; }
171 public virtual XmlResolver XmlResolver {
172 set { throw new NotImplementedException(); }
180 public override XmlNode CloneNode (bool deep)
182 throw new NotImplementedException ();
185 public XmlAttribute CreateAttribute (string name)
187 return CreateAttribute (name, String.Empty);
190 public XmlAttribute CreateAttribute (string qualifiedName, string namespaceURI)
195 ParseName (qualifiedName, out prefix, out localName);
197 return CreateAttribute (prefix, localName, namespaceURI);
200 public virtual XmlAttribute CreateAttribute (string prefix, string localName, string namespaceURI)
202 if ((localName == null) || (localName == String.Empty))
203 throw new ArgumentException ("The attribute local name cannot be empty.");
205 return new XmlAttribute (prefix, localName, namespaceURI, this);
208 public virtual XmlCDataSection CreateCDataSection (string data)
210 return new XmlCDataSection (data, this);
213 public virtual XmlComment CreateComment (string data)
215 return new XmlComment(data, this);
219 protected internal virtual XmlAttribute CreateDefaultAttribute (string prefix, string localName, string namespaceURI)
221 throw new NotImplementedException ();
224 public virtual XmlDocumentFragment CreateDocumentFragment ()
226 return new XmlDocumentFragment(this);
229 public virtual XmlDocumentType CreateDocumentType (string name, string publicId,
230 string systemId, string internalSubset)
232 return new XmlDocumentType (name, publicId, systemId, internalSubset, this);
235 public XmlElement CreateElement (string name)
237 return CreateElement (name, String.Empty);
240 public XmlElement CreateElement (
241 string qualifiedName,
247 ParseName (qualifiedName, out prefix, out localName);
249 return CreateElement (prefix, localName, namespaceURI);
252 public virtual XmlElement CreateElement (
257 if ((localName == null) || (localName == String.Empty))
258 throw new ArgumentException ("The local name for elements or attributes cannot be null or an empty string.");
260 return new XmlElement (prefix != null ? prefix : String.Empty, localName, namespaceURI != null ? namespaceURI : String.Empty, this);
264 public virtual XmlEntityReference CreateEntityReference (string name)
266 throw new NotImplementedException ();
270 protected internal virtual XPathNavigator CreateNavigator (XmlNode node)
272 throw new NotImplementedException ();
275 public virtual XmlNode CreateNode (
276 string nodeTypeString,
280 return CreateNode (GetNodeTypeFromString (nodeTypeString), name, namespaceURI);
283 public virtual XmlNode CreateNode (
288 string prefix = null;
289 string localName = name;
291 if ((type == XmlNodeType.Attribute) || (type == XmlNodeType.Element) || (type == XmlNodeType.EntityReference))
292 ParseName (name, out prefix, out localName);
294 return CreateNode (type, prefix, localName, namespaceURI);
297 public virtual XmlNode CreateNode (
304 case XmlNodeType.Attribute: return CreateAttribute (prefix, name, namespaceURI);
305 case XmlNodeType.CDATA: return CreateCDataSection (null);
306 case XmlNodeType.Comment: return CreateComment (null);
307 case XmlNodeType.Document: return new XmlDocument (); // TODO - test to see which constructor to use, i.e. use existing NameTable or not.
308 case XmlNodeType.DocumentFragment: return CreateDocumentFragment ();
309 case XmlNodeType.DocumentType: return CreateDocumentType (null, null, null, null);
310 case XmlNodeType.Element: return CreateElement (prefix, name, namespaceURI);
311 case XmlNodeType.EntityReference: return CreateEntityReference (null);
312 case XmlNodeType.ProcessingInstruction: return CreateProcessingInstruction (null, null);
313 case XmlNodeType.SignificantWhitespace: return CreateSignificantWhitespace (String.Empty);
314 case XmlNodeType.Text: return CreateTextNode (null);
315 case XmlNodeType.Whitespace: return CreateWhitespace (String.Empty);
316 case XmlNodeType.XmlDeclaration: return CreateXmlDeclaration ("1.0", null, null);
317 default: throw new ArgumentOutOfRangeException(String.Format("{0}\nParameter name: {1}",
318 "Specified argument was out of the range of valid values", type.ToString ()));
322 public virtual XmlProcessingInstruction CreateProcessingInstruction (
326 return new XmlProcessingInstruction (target, data, this);
329 public virtual XmlSignificantWhitespace CreateSignificantWhitespace (string text)
331 foreach (char c in text)
332 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
333 throw new ArgumentException ("Invalid whitespace characters.");
335 return new XmlSignificantWhitespace (text, this);
338 public virtual XmlText CreateTextNode (string text)
340 return new XmlText (text, this);
343 public virtual XmlWhitespace CreateWhitespace (string text)
345 foreach (char c in text)
346 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
347 throw new ArgumentException ("Invalid whitespace characters.");
349 return new XmlWhitespace (text, this);
352 public virtual XmlDeclaration CreateXmlDeclaration (string version, string encoding,
355 if (version != "1.0")
356 throw new ArgumentException ("version string is not correct.");
358 if ((standalone != null && standalone != String.Empty) && !((standalone == "yes") || (standalone == "no")))
359 throw new ArgumentException ("standalone string is not correct.");
361 return new XmlDeclaration (version, encoding, standalone, this);
365 public virtual XmlElement GetElementById (string elementId)
367 throw new NotImplementedException ();
370 public virtual XmlNodeList GetElementsByTagName (string name)
372 ArrayList nodeArrayList = new ArrayList ();
373 this.searchNodesRecursively (this, name, nodeArrayList);
374 return new XmlNodeArrayList (nodeArrayList);
377 private void searchNodesRecursively (XmlNode argNode, string argName,
378 ArrayList argArrayList)
380 XmlNodeList xmlNodeList = argNode.ChildNodes;
381 foreach (XmlNode node in xmlNodeList){
382 if (node.Name.Equals (argName))
383 argArrayList.Add (node);
385 this.searchNodesRecursively (node, argName, argArrayList);
389 private void searchNodesRecursively (XmlNode argNode, string argName, string argNamespaceURI,
390 ArrayList argArrayList)
392 XmlNodeList xmlNodeList = argNode.ChildNodes;
393 foreach (XmlNode node in xmlNodeList){
394 if (node.LocalName.Equals (argName) && node.NamespaceURI.Equals (argNamespaceURI))
395 argArrayList.Add (node);
397 this.searchNodesRecursively (node, argName, argNamespaceURI, argArrayList);
401 public virtual XmlNodeList GetElementsByTagName (string localName, string namespaceURI)
403 ArrayList nodeArrayList = new ArrayList ();
404 this.searchNodesRecursively (this, localName, namespaceURI, nodeArrayList);
405 return new XmlNodeArrayList (nodeArrayList);
408 private XmlNodeType GetNodeTypeFromString (string nodeTypeString)
410 switch (nodeTypeString) {
411 case "attribute": return XmlNodeType.Attribute;
412 case "cdatasection": return XmlNodeType.CDATA;
413 case "comment": return XmlNodeType.Comment;
414 case "document": return XmlNodeType.Document;
415 case "documentfragment": return XmlNodeType.DocumentFragment;
416 case "documenttype": return XmlNodeType.DocumentType;
417 case "element": return XmlNodeType.Element;
418 case "entityreference": return XmlNodeType.EntityReference;
419 case "processinginstruction": return XmlNodeType.ProcessingInstruction;
420 case "significantwhitespace": return XmlNodeType.SignificantWhitespace;
421 case "text": return XmlNodeType.Text;
422 case "whitespace": return XmlNodeType.Whitespace;
424 throw new ArgumentException(String.Format("The string doesn't represent any node type : {0}.", nodeTypeString));
428 [MonoTODO("default attributes (of imported doc); EntityReferences; Entity; Notation")]
429 public virtual XmlNode ImportNode (XmlNode node, bool deep)
431 switch(node.NodeType)
433 case XmlNodeType.Attribute:
435 XmlAttribute src_att = node as XmlAttribute;
436 XmlAttribute dst_att = this.CreateAttribute(src_att.Prefix, src_att.LocalName, src_att.NamespaceURI);
437 dst_att.Value = src_att.Value; // always explicitly specified (whether source is specified or not)
441 case XmlNodeType.CDATA:
442 return this.CreateCDataSection(node.Value);
444 case XmlNodeType.Comment:
445 return this.CreateComment(node.Value);
447 case XmlNodeType.Document:
448 throw new XmlException("Document cannot be imported.");
450 case XmlNodeType.DocumentFragment:
452 XmlDocumentFragment df = this.CreateDocumentFragment();
455 foreach(XmlNode n in node.ChildNodes)
457 df.AppendChild(this.ImportNode(n, deep));
463 case XmlNodeType.DocumentType:
464 throw new XmlException("DocumentType cannot be imported.");
466 case XmlNodeType.Element:
468 XmlElement src = (XmlElement)node;
469 XmlElement dst = this.CreateElement(src.Prefix, src.LocalName, src.NamespaceURI);
470 foreach(XmlAttribute attr in src.Attributes)
472 if(attr.Specified) // copies only specified attributes
473 dst.SetAttributeNode((XmlAttribute)this.ImportNode(attr, deep));
474 if(DocumentType != null)
476 // TODO: create default attribute values
481 foreach(XmlNode n in src.ChildNodes)
482 dst.AppendChild(this.ImportNode(n, deep));
487 case XmlNodeType.EndElement:
488 throw new XmlException ("Illegal ImportNode call for NodeType.EndElement");
489 case XmlNodeType.EndEntity:
490 throw new XmlException ("Illegal ImportNode call for NodeType.EndEntity");
492 case XmlNodeType.Entity:
493 throw new NotImplementedException (); // TODO
495 // [2002.10.14] CreateEntityReference not implemented.
496 case XmlNodeType.EntityReference:
497 throw new NotImplementedException("ImportNode of EntityReference not implemented mainly because CreateEntityReference was implemented in the meantime.");
498 // return this.CreateEntityReference(node.Name);
500 case XmlNodeType.None:
501 throw new XmlException ("Illegal ImportNode call for NodeType.None");
503 case XmlNodeType.Notation:
504 throw new NotImplementedException (); // TODO
506 case XmlNodeType.ProcessingInstruction:
507 XmlProcessingInstruction pi = node as XmlProcessingInstruction;
508 return this.CreateProcessingInstruction(pi.Target, pi.Data);
510 case XmlNodeType.SignificantWhitespace:
511 return this.CreateSignificantWhitespace(node.Value);
513 case XmlNodeType.Text:
514 return this.CreateTextNode(node.Value);
516 case XmlNodeType.Whitespace:
517 return this.CreateWhitespace(node.Value);
519 case XmlNodeType.XmlDeclaration:
520 XmlDeclaration srcDecl = node as XmlDeclaration;
521 return this.CreateXmlDeclaration(srcDecl.Version, srcDecl.Encoding, srcDecl.Standalone);
524 throw new NotImplementedException ();
528 public virtual void Load (Stream inStream)
530 XmlReader xmlReader = new XmlTextReader (inStream);
534 public virtual void Load (string filename)
537 XmlReader xmlReader = new XmlTextReader (new StreamReader (filename));
541 public virtual void Load (TextReader txtReader)
543 Load (new XmlTextReader (txtReader));
546 public virtual void Load (XmlReader xmlReader)
548 // Reset our document
549 // For now this just means removing all our children but later this
550 // may turn out o need to call a private method that resets other things
551 // like properties we have, etc.
554 XmlNode currentNode = this;
556 // This method of XmlNode is previously written here.
557 // Then I(ginga) moved them to use this logic with XmlElement.
558 this.ConstructDOM(xmlReader, currentNode);
561 public virtual void LoadXml (string xml)
563 XmlReader xmlReader = new XmlTextReader (new StringReader (xml));
567 internal void onNodeChanged (XmlNode node, XmlNode Parent)
569 if (NodeChanged != null)
570 NodeChanged (node, new XmlNodeChangedEventArgs
571 (XmlNodeChangedAction.Change,
572 node, Parent, Parent));
575 internal void onNodeChanging(XmlNode node, XmlNode Parent)
577 if (NodeChanging != null)
578 NodeChanging (node, new XmlNodeChangedEventArgs
579 (XmlNodeChangedAction.Change,
580 node, Parent, Parent));
583 internal void onNodeInserted (XmlNode node, XmlNode newParent)
585 if (NodeInserted != null)
586 NodeInserted (node, new XmlNodeChangedEventArgs
587 (XmlNodeChangedAction.Insert,
588 node, null, newParent));
591 internal void onNodeInserting (XmlNode node, XmlNode newParent)
593 if (NodeInserting != null)
594 NodeInserting (node, new XmlNodeChangedEventArgs
595 (XmlNodeChangedAction.Insert,
596 node, null, newParent));
599 internal void onNodeRemoved (XmlNode node, XmlNode oldParent)
601 if (NodeRemoved != null)
602 NodeRemoved (node, new XmlNodeChangedEventArgs
603 (XmlNodeChangedAction.Remove,
604 node, oldParent, null));
607 internal void onNodeRemoving (XmlNode node, XmlNode oldParent)
609 if (NodeRemoving != null)
610 NodeRemoving (node, new XmlNodeChangedEventArgs
611 (XmlNodeChangedAction.Remove,
612 node, oldParent, null));
615 private void ParseName (string name, out string prefix, out string localName)
617 int indexOfColon = name.IndexOf (':');
619 if (indexOfColon != -1) {
620 prefix = name.Substring (0, indexOfColon);
621 localName = name.Substring (indexOfColon + 1);
629 public virtual XmlNode ReadNode(XmlReader reader)
631 throw new NotImplementedException ();
634 [MonoTODO ("Verify what encoding is used by default; Should use PreserveWhiteSpace")]
635 public virtual void Save(Stream outStream)
637 // To implementor: utf-8 is OK, at least for (ginga's) Japanese environment.
638 XmlTextWriter xmlWriter = new XmlTextWriter (outStream, Encoding.UTF8);
639 WriteContentTo (xmlWriter);
643 [MonoTODO ("Verify what encoding is used by default; Should use PreseveWhiteSpace")]
644 public virtual void Save (string filename)
646 // To implementor: utf-8 is OK, at least for (ginga's) Japanese environment.
647 XmlTextWriter xmlWriter = new XmlTextWriter (filename, Encoding.UTF8);
648 WriteContentTo (xmlWriter);
653 public virtual void Save (TextWriter writer)
655 XmlTextWriter xmlWriter = new XmlTextWriter (writer);
656 WriteContentTo (xmlWriter);
660 [MonoTODO ("Should preserve white space if PreserveWhisspace is set")]
661 public virtual void Save (XmlWriter xmlWriter)
664 // This should preserve white space if PreserveWhiteSpace is true
666 WriteContentTo (xmlWriter);
670 public override void WriteContentTo (XmlWriter w)
672 foreach(XmlNode childNode in ChildNodes)
673 childNode.WriteTo(w);
676 public override void WriteTo (XmlWriter w)
681 private void AddDefaultNameTableKeys()
683 // The following keys are default of MS .NET Framework
684 nameTable.Add("#text");
685 nameTable.Add("xml");
686 nameTable.Add("xmlns");
687 nameTable.Add("#entity");
688 nameTable.Add("#document-fragment");
689 nameTable.Add("#comment");
690 nameTable.Add("space");
692 nameTable.Add("#whitespace");
693 nameTable.Add("http://www.w3.org/2000/xmlns/");
694 nameTable.Add("#cdata-section");
695 nameTable.Add("lang");
696 nameTable.Add("#document");
697 nameTable.Add("#significant-whitespace");