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;
37 public XmlDocument () : base (null)
39 implementation = new XmlImplementation();
40 nameTable = implementation.internalNameTable;
41 AddDefaultNameTableKeys();
44 protected internal XmlDocument (XmlImplementation imp) : base (null)
47 nameTable = imp.internalNameTable;
48 AddDefaultNameTableKeys();
51 public XmlDocument (XmlNameTable nt) : base (null)
53 implementation = new XmlImplementation();
54 implementation.internalNameTable = nt;
56 AddDefaultNameTableKeys();
63 public event XmlNodeChangedEventHandler NodeChanged;
65 public event XmlNodeChangedEventHandler NodeChanging;
67 public event XmlNodeChangedEventHandler NodeInserted;
69 public event XmlNodeChangedEventHandler NodeInserting;
71 public event XmlNodeChangedEventHandler NodeRemoved;
73 public event XmlNodeChangedEventHandler NodeRemoving;
79 public override string BaseURI {
85 public XmlElement DocumentElement {
87 XmlNode node = FirstChild;
89 while (node != null) {
90 if (node is XmlElement)
92 node = node.NextSibling;
95 return node != null ? node as XmlElement : null;
99 [MonoTODO("It doesn't have internal subset object model.")]
100 public virtual XmlDocumentType DocumentType {
102 foreach(XmlNode n in this.ChildNodes) {
103 if(n.NodeType == XmlNodeType.DocumentType)
104 return (XmlDocumentType)n;
110 public XmlImplementation Implementation {
111 get { return implementation; }
114 [MonoTODO ("Setter.")]
115 public override string InnerXml {
117 // Not sure why this is an override. Passing through for now.
118 // ... Maybe its setter may be an override :-) [ginga]
119 return base.InnerXml;
121 set { throw new NotImplementedException(); }
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 {
165 public bool PreserveWhitespace {
166 get { throw new NotImplementedException(); }
167 set { throw new NotImplementedException(); }
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 ();
225 public virtual XmlDocumentFragment CreateDocumentFragment ()
227 return new XmlDocumentFragment(this);
228 // throw new NotImplementedException ();
231 public virtual XmlDocumentType CreateDocumentType (string name, string publicId,
232 string systemId, string internalSubset)
234 return new XmlDocumentType (name, publicId, systemId, internalSubset, this);
237 public XmlElement CreateElement (string name)
239 return CreateElement (name, String.Empty);
242 public XmlElement CreateElement (
243 string qualifiedName,
249 ParseName (qualifiedName, out prefix, out localName);
251 return CreateElement (prefix, localName, namespaceURI);
254 public virtual XmlElement CreateElement (
259 if ((localName == null) || (localName == String.Empty))
260 throw new ArgumentException ("The local name for elements or attributes cannot be null or an empty string.");
262 return new XmlElement (prefix != null ? prefix : String.Empty, localName, namespaceURI != null ? namespaceURI : String.Empty, this);
266 public virtual XmlEntityReference CreateEntityReference (string name)
268 throw new NotImplementedException ();
272 protected internal virtual XPathNavigator CreateNavigator (XmlNode node)
274 throw new NotImplementedException ();
277 public virtual XmlNode CreateNode (
278 string nodeTypeString,
282 return CreateNode (GetNodeTypeFromString (nodeTypeString), name, namespaceURI);
285 public virtual XmlNode CreateNode (
290 string prefix = null;
291 string localName = name;
293 if ((type == XmlNodeType.Attribute) || (type == XmlNodeType.Element) || (type == XmlNodeType.EntityReference))
294 ParseName (name, out prefix, out localName);
296 return CreateNode (type, prefix, localName, namespaceURI);
299 public virtual XmlNode CreateNode (
306 case XmlNodeType.Attribute: return CreateAttribute (prefix, name, namespaceURI);
307 case XmlNodeType.CDATA: return CreateCDataSection (null);
308 case XmlNodeType.Comment: return CreateComment (null);
309 case XmlNodeType.Document: return new XmlDocument (); // TODO - test to see which constructor to use, i.e. use existing NameTable or not.
310 case XmlNodeType.DocumentFragment: return CreateDocumentFragment ();
311 case XmlNodeType.DocumentType: return CreateDocumentType (null, null, null, null);
312 case XmlNodeType.Element: return CreateElement (prefix, name, namespaceURI);
313 case XmlNodeType.EntityReference: return CreateEntityReference (null);
314 case XmlNodeType.ProcessingInstruction: return CreateProcessingInstruction (null, null);
315 case XmlNodeType.SignificantWhitespace: return CreateSignificantWhitespace (String.Empty);
316 case XmlNodeType.Text: return CreateTextNode (null);
317 case XmlNodeType.Whitespace: return CreateWhitespace (String.Empty);
318 case XmlNodeType.XmlDeclaration: return CreateXmlDeclaration ("1.0", null, null);
319 default: throw new ArgumentOutOfRangeException(String.Format("{0}\nParameter name: {1}",
320 "Specified argument was out of the range of valid values", type.ToString ()));
324 public virtual XmlProcessingInstruction CreateProcessingInstruction (
328 return new XmlProcessingInstruction (target, data, this);
331 public virtual XmlSignificantWhitespace CreateSignificantWhitespace (string text)
333 foreach (char c in text)
334 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
335 throw new ArgumentException ("Invalid whitespace characters.");
337 return new XmlSignificantWhitespace (text, this);
340 public virtual XmlText CreateTextNode (string text)
342 return new XmlText (text, this);
345 public virtual XmlWhitespace CreateWhitespace (string text)
347 foreach (char c in text)
348 if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t'))
349 throw new ArgumentException ("Invalid whitespace characters.");
351 return new XmlWhitespace (text, this);
354 public virtual XmlDeclaration CreateXmlDeclaration (string version, string encoding,
357 if (version != "1.0")
358 throw new ArgumentException ("version string is not correct.");
360 if ((standalone != null && standalone != String.Empty) && !((standalone == "yes") || (standalone == "no")))
361 throw new ArgumentException ("standalone string is not correct.");
363 return new XmlDeclaration (version, encoding, standalone, this);
367 public virtual XmlElement GetElementById (string elementId)
369 throw new NotImplementedException ();
372 public virtual XmlNodeList GetElementsByTagName (string name)
374 ArrayList nodeArrayList = new ArrayList ();
375 this.searchNodesRecursively (this, name, nodeArrayList);
376 return new XmlNodeArrayList (nodeArrayList);
379 private void searchNodesRecursively (XmlNode argNode, string argName,
380 ArrayList argArrayList)
382 XmlNodeList xmlNodeList = argNode.ChildNodes;
383 foreach (XmlNode node in xmlNodeList){
384 if (node.Name.Equals (argName))
385 argArrayList.Add (node);
387 this.searchNodesRecursively (node, argName, argArrayList);
391 private void searchNodesRecursively (XmlNode argNode, string argName, string argNamespaceURI,
392 ArrayList argArrayList)
394 XmlNodeList xmlNodeList = argNode.ChildNodes;
395 foreach (XmlNode node in xmlNodeList){
396 if (node.LocalName.Equals (argName) && node.NamespaceURI.Equals (argNamespaceURI))
397 argArrayList.Add (node);
399 this.searchNodesRecursively (node, argName, argNamespaceURI, argArrayList);
403 public virtual XmlNodeList GetElementsByTagName (string localName, string namespaceURI)
405 ArrayList nodeArrayList = new ArrayList ();
406 this.searchNodesRecursively (this, localName, namespaceURI, nodeArrayList);
407 return new XmlNodeArrayList (nodeArrayList);
410 private XmlNodeType GetNodeTypeFromString (string nodeTypeString)
412 switch (nodeTypeString) {
413 case "attribute": return XmlNodeType.Attribute;
414 case "cdatasection": return XmlNodeType.CDATA;
415 case "comment": return XmlNodeType.Comment;
416 case "document": return XmlNodeType.Document;
417 case "documentfragment": return XmlNodeType.DocumentFragment;
418 case "documenttype": return XmlNodeType.DocumentType;
419 case "element": return XmlNodeType.Element;
420 case "entityreference": return XmlNodeType.EntityReference;
421 case "processinginstruction": return XmlNodeType.ProcessingInstruction;
422 case "significantwhitespace": return XmlNodeType.SignificantWhitespace;
423 case "text": return XmlNodeType.Text;
424 case "whitespace": return XmlNodeType.Whitespace;
426 throw new ArgumentException(String.Format("The string doesn't represent any node type : {0}.", nodeTypeString));
430 [MonoTODO("default attributes (of imported doc); EntityReferences; Entity; Notation")]
431 public virtual XmlNode ImportNode (XmlNode node, bool deep)
433 switch(node.NodeType)
435 case XmlNodeType.Attribute:
437 XmlAttribute src_att = node as XmlAttribute;
438 XmlAttribute dst_att = this.CreateAttribute(src_att.Prefix, src_att.LocalName, src_att.NamespaceURI);
439 dst_att.Value = src_att.Value; // always explicitly specified (whether source is specified or not)
443 case XmlNodeType.CDATA:
444 return this.CreateCDataSection(node.Value);
446 case XmlNodeType.Comment:
447 return this.CreateComment(node.Value);
449 case XmlNodeType.Document:
450 throw new XmlException("Document cannot be imported.");
452 case XmlNodeType.DocumentFragment:
454 XmlDocumentFragment df = this.CreateDocumentFragment();
457 foreach(XmlNode n in node.ChildNodes)
459 df.AppendChild(this.ImportNode(n, deep));
465 case XmlNodeType.DocumentType:
466 throw new XmlException("DocumentType cannot be imported.");
468 case XmlNodeType.Element:
470 XmlElement src = (XmlElement)node;
471 XmlElement dst = this.CreateElement(src.Prefix, src.LocalName, src.NamespaceURI);
472 foreach(XmlAttribute attr in src.Attributes)
474 if(attr.Specified) // copies only specified attributes
475 dst.SetAttributeNode((XmlAttribute)this.ImportNode(attr, deep));
476 if(DocumentType != null)
478 // TODO: create default attribute values
483 foreach(XmlNode n in src.ChildNodes)
484 dst.AppendChild(this.ImportNode(n, deep));
489 case XmlNodeType.EndElement:
490 throw new XmlException ("Illegal ImportNode call for NodeType.EndElement");
491 case XmlNodeType.EndEntity:
492 throw new XmlException ("Illegal ImportNode call for NodeType.EndEntity");
494 case XmlNodeType.Entity:
495 throw new NotImplementedException (); // TODO
497 // [2002.10.14] CreateEntityReference not implemented.
498 case XmlNodeType.EntityReference:
499 throw new NotImplementedException("ImportNode of EntityReference not implemented mainly because CreateEntityReference was implemented in the meantime.");
500 // return this.CreateEntityReference(node.Name);
502 case XmlNodeType.None:
503 throw new XmlException ("Illegal ImportNode call for NodeType.None");
505 case XmlNodeType.Notation:
506 throw new NotImplementedException (); // TODO
508 case XmlNodeType.ProcessingInstruction:
509 XmlProcessingInstruction pi = node as XmlProcessingInstruction;
510 return this.CreateProcessingInstruction(pi.Target, pi.Data);
512 case XmlNodeType.SignificantWhitespace:
513 return this.CreateSignificantWhitespace(node.Value);
515 case XmlNodeType.Text:
516 return this.CreateTextNode(node.Value);
518 case XmlNodeType.Whitespace:
519 return this.CreateWhitespace(node.Value);
521 case XmlNodeType.XmlDeclaration:
522 XmlDeclaration srcDecl = node as XmlDeclaration;
523 return this.CreateXmlDeclaration(srcDecl.Version, srcDecl.Encoding, srcDecl.Standalone);
526 throw new NotImplementedException ();
530 public virtual void Load (Stream inStream)
532 XmlReader xmlReader = new XmlTextReader (inStream);
536 public virtual void Load (string filename)
539 XmlReader xmlReader = new XmlTextReader (new StreamReader (filename));
543 public virtual void Load (TextReader txtReader)
545 Load (new XmlTextReader (txtReader));
548 public virtual void Load (XmlReader xmlReader)
550 // Reset our document
551 // For now this just means removing all our children but later this
552 // may turn out o need to call a private method that resets other things
553 // like properties we have, etc.
556 XmlNode currentNode = this;
558 // This method of XmlNode is previously written here.
559 // Then I(ginga) moved them to use this logic with XmlElement.
560 this.ConstructDOM(xmlReader, currentNode);
563 public virtual void LoadXml (string xml)
565 XmlReader xmlReader = new XmlTextReader (new StringReader (xml));
569 internal void onNodeChanged (XmlNode node, XmlNode Parent)
571 if (NodeChanged != null)
572 NodeChanged (node, new XmlNodeChangedEventArgs
573 (XmlNodeChangedAction.Change,
574 node, Parent, Parent));
577 internal void onNodeChanging(XmlNode node, XmlNode Parent)
579 if (NodeChanging != null)
580 NodeChanging (node, new XmlNodeChangedEventArgs
581 (XmlNodeChangedAction.Change,
582 node, Parent, Parent));
585 internal void onNodeInserted (XmlNode node, XmlNode newParent)
587 if (NodeInserted != null)
588 NodeInserted (node, new XmlNodeChangedEventArgs
589 (XmlNodeChangedAction.Insert,
590 node, null, newParent));
593 internal void onNodeInserting (XmlNode node, XmlNode newParent)
595 if (NodeInserting != null)
596 NodeInserting (node, new XmlNodeChangedEventArgs
597 (XmlNodeChangedAction.Insert,
598 node, null, newParent));
601 internal void onNodeRemoved (XmlNode node, XmlNode oldParent)
603 if (NodeRemoved != null)
604 NodeRemoved (node, new XmlNodeChangedEventArgs
605 (XmlNodeChangedAction.Remove,
606 node, oldParent, null));
609 internal void onNodeRemoving (XmlNode node, XmlNode oldParent)
611 if (NodeRemoving != null)
612 NodeRemoving (node, new XmlNodeChangedEventArgs
613 (XmlNodeChangedAction.Remove,
614 node, oldParent, null));
617 private void ParseName (string name, out string prefix, out string localName)
619 int indexOfColon = name.IndexOf (':');
621 if (indexOfColon != -1) {
622 prefix = name.Substring (0, indexOfColon);
623 localName = name.Substring (indexOfColon + 1);
631 public virtual XmlNode ReadNode(XmlReader reader)
633 throw new NotImplementedException ();
636 [MonoTODO ("Verify what encoding is used by default; Should use PreserveWhiteSpace")]
637 public virtual void Save(Stream outStream)
639 // To implementor: utf-8 is OK, at least for (ginga's) Japanese environment.
640 XmlTextWriter xmlWriter = new XmlTextWriter (outStream, Encoding.UTF8);
641 WriteContentTo (xmlWriter);
645 [MonoTODO ("Verify what encoding is used by default; Should use PreseveWhiteSpace")]
646 public virtual void Save (string filename)
648 // To implementor: utf-8 is OK, at least for (ginga's) Japanese environment.
649 XmlTextWriter xmlWriter = new XmlTextWriter (filename, Encoding.UTF8);
650 WriteContentTo (xmlWriter);
655 public virtual void Save (TextWriter writer)
657 XmlTextWriter xmlWriter = new XmlTextWriter (writer);
658 WriteContentTo (xmlWriter);
662 [MonoTODO ("Should preserve white space if PreserveWhisspace is set")]
663 public virtual void Save (XmlWriter xmlWriter)
666 // This should preserve white space if PreserveWhiteSpace is true
668 WriteContentTo (xmlWriter);
672 public override void WriteContentTo (XmlWriter w)
674 foreach(XmlNode childNode in ChildNodes)
675 childNode.WriteTo(w);
678 public override void WriteTo (XmlWriter w)
683 private void AddDefaultNameTableKeys()
685 // The following keys are default of MS .NET Framework
686 nameTable.Add("#text");
687 nameTable.Add("xml");
688 nameTable.Add("xmlns");
689 nameTable.Add("#entity");
690 nameTable.Add("#document-fragment");
691 nameTable.Add("#comment");
692 nameTable.Add("space");
694 nameTable.Add("#whitespace");
695 nameTable.Add("http://www.w3.org/2000/xmlns/");
696 nameTable.Add("#cdata-section");
697 nameTable.Add("lang");
698 nameTable.Add("#document");
699 nameTable.Add("#significant-whitespace");