1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
6 // Daniel Weber (daniel-weber@austin.rr.com)
8 // (C) 2001 Daniel Weber
11 using System.Collections;
12 using System.Xml.XPath;
16 public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable
18 //======= Private data members ==============================================
19 private XmlNodeListAsArrayList _childNodes;
20 protected XmlDocument FOwnerDocument;
21 protected XmlNode _parent;
24 // for <foo:bar xmlns:foo="http://www.foobar.com/schema/foobar">... </foo:bar>
25 // qualified name: foo:bar
26 // namespaceURI = "http://www.foobar.com/schema/foobar"
29 // Note that namespaces can be nested (child namespace != parent namespace)
30 // namespaces are optional
31 protected string Fname;
32 protected string FnamespaceURI;
33 protected string Fprefix;
34 protected string FlocalName;
36 // baseURI holds the location from which the document was loaded
37 // If the node was created from a document at c:\tmp.xml, then that's what will be here
38 protected string FbaseURI;
40 // value of the node (overriden in classes that do something different)
41 // default behavior is just to store it
42 protected string Fvalue;
44 //=====================================================================
45 // ============ Properties ============================================
46 //=====================================================================
48 /// Get the XmlAttributeCollection representing the attributes
49 /// on the node type. Returns null if the node type is not XmlElement.
51 public virtual XmlAttributeCollection Attributes
59 /// Return the base Uniform Resource Indicator (URI) used to resolve
60 /// this node, or String.Empty.
62 public virtual string BaseURI
71 /// Return all child nodes of this node. If there are no children,
72 /// return an empty XmlNodeList;
74 public virtual XmlNodeList ChildNodes
78 if (_childNodes == null)
79 _childNodes = new XmlNodeListAsArrayList();
81 return _childNodes as XmlNodeList;
86 /// Return first child node as XmlNode or null
87 /// if the node has no children
89 public virtual XmlNode FirstChild
93 if (ChildNodes.Count == 0)
101 /// Return true if the node has children
103 public virtual bool HasChildNodes
107 if (ChildNodes.Count == 0)
115 /// Get or Set the concatenated values of node and children
117 public virtual string InnerText
121 // TODO - implement set InnerText()
122 throw new NotImplementedException();
127 // TODO - implement set InnerText()
128 throw new NotImplementedException();
133 /// Get/Set the XML representing just the child nodes of this node
135 public virtual string InnerXml
139 // TODO - implement set InnerXml()
140 throw new NotImplementedException();
145 // TODO - implement set InnerXml()
146 throw new NotImplementedException();
151 /// Property Get - true if node is read-only
153 public virtual bool IsReadOnly
157 return OwnerDocument.IsReadOnly;
162 /// Return the child element named [string]. Returns XmlElement
163 /// Indexer for XmlNode class.
165 [System.Runtime.CompilerServices.IndexerNameAttribute("Item")]
166 public virtual XmlElement this [String index]
170 // TODO - implement XmlNode.Item(string)
171 throw new NotImplementedException();
176 /// Get the last child node, or null if there are no nodes
178 public virtual XmlNode LastChild
182 if (_childNodes.Count == 0)
185 return _childNodes.Item(_childNodes.Count - 1);
191 /// Returns the local name of the node with qualifiers removed
192 /// LocalName of ns:elementName = "elementName"
194 public abstract string LocalName {get;}
197 /// Get the qualified node name
198 /// derived classes must implement as behavior varies
201 public abstract string Name { get; }
204 /// Get the namespace URI or String.Empty if none
206 public virtual string NamespaceURI
210 // TODO - implement Namespace URI, or determine abstractness
211 throw new NotImplementedException("XmlNode.NamespaceURI not implemented");
216 /// Get the node immediatelly following this node, or null
218 public virtual XmlNode NextSibling
225 XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList;
226 int ourIndex = children.data.IndexOf(this);
227 return children[ourIndex + 1];
234 public virtual XmlNodeType NodeType
238 return XmlNodeType.None;
243 /// Return the string representing this node and all it's children
245 public virtual string OuterXml
249 // TODO - implement OuterXml {get;}
250 throw new NotImplementedException();
255 /// Return owning document.
256 /// If this nodeType is a document, return null
258 public virtual XmlDocument OwnerDocument
262 return FOwnerDocument;
267 /// Returns the parent node, or null
268 /// Return value depends on superclass node type
270 public virtual XmlNode ParentNode
279 /// set/get the namespace prefix for this node, or
280 /// string.empty if it does not exist
282 public virtual string Prefix
291 // TODO - validation on XmlNode.Prefix {set;}? (no)
297 /// The preceding XmlNode or null
299 public virtual XmlNode PreviousSibling {
304 XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList;
305 int ourIndex = children.data.IndexOf(this);
306 return children[ourIndex - 1];
314 /// Get/Set the value for this node
316 public virtual string Value
329 //=====================================================================
330 //======= Methods =====================================================
331 //=====================================================================
333 /// Appends the specified node to the end of the child node list
335 /// <param name="newChild"></param>
336 /// <returns></returns>
337 public virtual XmlNode AppendChild (XmlNode newChild)
339 ((XmlNodeListAsArrayList)ChildNodes).Add(newChild);
344 /// Return a clone of this node
346 /// <returns></returns>
347 public virtual object Clone()
349 // TODO - implement XmlNode.Clone() as object
350 throw new NotImplementedException("object XmlNode.Clone() not implmented");
354 /// Return a clone of the node
356 /// <param name="deep">Make copy of all children</param>
357 /// <returns>Cloned node</returns>
358 public abstract XmlNode CloneNode( bool deep);
361 /// Return an XPathNavigator for navigating this node
363 /// <returns></returns>
364 public System.Xml.XPath.XPathNavigator CreateNavigator()
366 // TODO - implement CreateNavigator()
367 throw new NotImplementedException();
371 /// Provide support for "for each"
373 /// <returns></returns>
374 public IEnumerator GetEnumerator()
376 return _childNodes.data.GetEnumerator();
380 /// Look up the closest namespace for this node that is in scope for the given prefix
382 /// <param name="prefix"></param>
383 /// <returns>Namespace URI</returns>
384 public virtual string GetNamespaceOfPrefix(string prefix)
386 // TODO - implement GetNamespaceOfPrefix()
387 throw new NotImplementedException();
391 /// Get the closest xmlns declaration for the given namespace URI that is in scope.
392 /// Returns the prefix defined in that declaration.
394 /// <param name="namespaceURI"></param>
395 /// <returns></returns>
396 public virtual string GetPrefixOfNamespace(string namespaceURI)
398 // TODO - implement GetPrefixOfNamespace
399 throw new NotImplementedException();
403 /// Insert newChild directly after the reference node.
404 /// If refChild is null, newChild is inserted at the beginning of childnodes.
405 /// If newChild is a document fragment, all nodes are inserted after refChild.
406 /// If newChild is already in the tree, it is first removed.
408 /// <exception cref="ArgumentException">NewChild was created from differant document.
409 /// RefChild not a child of this node or null.
410 /// Node is read-only</exception>
411 /// <exception cref="InvalidOperationException">Node is of type that does not have children
412 /// Node to insert is an ancestor of this node.</exception>
413 /// <param name="newChild">Child node to insert.</param>
414 /// <param name="refChild">Reference node to insert after</param>
415 /// <returns>Removed node, or null if no node removed.</returns>
416 public virtual XmlNode InsertAfter(XmlNode newChild, XmlNode refChild)
418 // Checks parent not ancestor, arguments valid, etc. Throws exception on error
419 InsertionCheck(newChild, refChild);
421 // Scan the node list, looking for refChild and seeing if newChild is in the list
422 // Note that if refNode is null (prepend), we don't want to do the .Equals(null)
423 XmlNode retval = null;
424 int refNodeIndex = -1;
426 for (int i = 0; i < _childNodes.Count; i++)
428 XmlNode e = _childNodes.data[i] as XmlNode;
429 if (e.Equals(newChild))
432 FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode);
433 _childNodes.data.RemoveAt(i);
434 newChild.setParent(null);
435 FOwnerDocument.onNodeRemoved(newChild, null);
440 if ( (refChild != null ) & ( e.Equals(refChild) ) )
449 if ( ( refNodeIndex == -1 ) & (refChild != null) )
450 throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()");
452 FOwnerDocument.onNodeInserting(newChild, this);
454 if (refChild == null)
457 refNodeIndex++; // insert after reference...
459 if (newChild.NodeType == XmlNodeType.DocumentFragment)
461 // Insert all children, starting from refNodeIndex (0,1,2...n)
462 for (int i = 0; i < newChild.ChildNodes.Count; i++)
464 XmlNode e = newChild.ChildNodes[i] as XmlNode;
465 FOwnerDocument.onNodeInserting(e, this);
466 _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]);
468 FOwnerDocument.onNodeInserted(newChild, this);
474 FOwnerDocument.onNodeInserting(newChild, this);
475 _childNodes.data.Insert(refNodeIndex, newChild);
476 newChild.setParent(this);
477 FOwnerDocument.onNodeInserted(newChild, this);
484 /// Insert newChild directly before the reference node.
485 /// If refChild is null, newChild is inserted at the end of childnodes.
486 /// If newChild is a document fragment, all nodes are inserted before refChild.
487 /// If newChild is already in the tree, it is first removed.
489 /// <exception cref="ArgumentException">NewChild was created from different document.
490 /// RefChild not a child of this node, or is null.
491 /// Node is read-only</exception>
492 /// <exception cref="InvalidOperationException">Node is of type that does not have children.
493 /// Node to insert is an ancestor of this node.</exception>
494 /// <param name="newChild">Child node to insert.</param>
495 /// <param name="refChild">Reference node to insert after</param>
496 /// <returns>Removed node, or null if no node removed.</returns>
497 public virtual XmlNode InsertBefore(XmlNode newChild, XmlNode refChild)
499 // Checks parent not ancestor, arguments valid, etc. Throws exception on error
500 InsertionCheck(newChild, refChild);
502 // Scan the node list, looking for refChild and seeing if newChild is in the list
503 XmlNode retval = null;
504 int refNodeIndex = -1;
506 for (int i = 0; i < _childNodes.Count; i++)
508 XmlNode e = _childNodes.data[i] as XmlNode;
509 if (e.Equals(newChild))
512 FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode);
513 _childNodes.data.RemoveAt(i);
514 newChild.setParent(null);
515 FOwnerDocument.onNodeRemoved(newChild, null);
519 if ( (refChild != null ) & ( e.Equals(refChild) ) )
528 if ( ( refNodeIndex == -1 ) & (refChild != null) )
529 throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()");
531 if (refChild == null)
532 refNodeIndex = _childNodes.Count;
534 if (newChild.NodeType == XmlNodeType.DocumentFragment)
536 // Insert all children, starting from refNodeIndex (0,1,2...n)
537 for (int i = 0; i < newChild.ChildNodes.Count; i++)
539 XmlNode e = newChild.ChildNodes[i] as XmlNode;
540 FOwnerDocument.onNodeInserting(e, this);
541 _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]);
543 FOwnerDocument.onNodeInserted(newChild, this);
549 newChild.OwnerDocument.onNodeInserting(newChild, this);
550 _childNodes.data.Insert(refNodeIndex, newChild);
551 newChild.setParent(this);
552 newChild.OwnerDocument.onNodeInserted(newChild, this);
559 /// Put all nodes under this node in "normal" form
560 /// Whatever that means...
562 public virtual void Normalize()
564 // TODO - Implement Normalize()
565 throw new NotImplementedException();
569 /// Add the specified child to the beginning of the child node list
571 /// <param name="newChild">Node to add</param>
572 /// <returns>The node added</returns>
573 public virtual XmlNode PrependChild(XmlNode newChild)
575 return InsertAfter(newChild, null);
579 /// Remove all children and attributes
581 public virtual void RemoveAll()
583 if (_childNodes == null)
587 // Remove in order, 0..n
588 while (_childNodes.Count > 0)
590 XmlNode e = _childNodes[0];
591 FOwnerDocument.onNodeRemoving(e, this);
593 _childNodes.data.RemoveAt(0);
594 FOwnerDocument.onNodeRemoved(e, null);
600 /// Remove specified child node
602 /// <param name="oldChild"></param>
603 /// <returns>Removed node</returns>
604 public virtual XmlNode RemoveChild(XmlNode oldChild)
606 // TODO - implement RemoveChild(oldChild)
607 throw new NotImplementedException();
611 /// Select a list of nodes matching the xpath
613 /// <param name="xpath"></param>
614 /// <returns>matching nodes</returns>
615 public XmlNodeList SelectNodes( string xpath)
617 // TODO - imlement SelectNodes(xpath)
618 throw new NotImplementedException();
622 /// Select a list of nodes matching the xpath. Any prefixes are resolved
623 /// using the passed namespace manager
625 /// <param name="xpath"></param>
626 /// <param name="nsmgr"></param>
627 /// <returns></returns>
628 public XmlNodeList SelectNodes(string xpath, XmlNamespaceManager nsmgr)
630 // TODO - implement SelectNodes(xpath, nsmgr)
631 throw new NotImplementedException();
635 /// Selects the first node that matches xpath
637 /// <param name="?"></param>
638 /// <returns></returns>
639 public XmlNode SelectSingleNode(string xpatch)
641 // TODO - implement SelectSingeNode(xpath)
642 throw new NotImplementedException();
646 /// Returns the first node that matches xpath
647 /// Uses the passed namespace manager to resolve namespace URI's
649 /// <param name="xpath"></param>
650 /// <param name="nsmgr"></param>
651 /// <returns></returns>
652 public XmlNode SelectSingleNode(string xpath, XmlNamespaceManager nsmgr)
654 // Implement SelectSingleNode(xpath, nsmgr)
655 throw new NotImplementedException();
659 /// Tests if the DOM implementation supports the passed feature
661 /// <param name="feature"></param>
662 /// <param name="version"></param>
663 /// <returns></returns>
664 public virtual bool Supports(string feature, string version)
666 //TODO - implement Supports(feature, version)
667 throw new NotImplementedException();
671 /// Returns a string representation of the current node and it's children
673 /// <returns></returns>
674 public override string ToString()
676 // TODO - implement ToString()
677 throw new NotImplementedException();
681 /// Saves all children of the current node to the passed writer
683 /// <param name="w"></param>
684 public abstract void WriteContentTo(XmlWriter w);
687 /// Saves the current node to writer w
689 /// <param name="w"></param>
690 public abstract void WriteTo(XmlWriter w);
692 //======= Internal methods ===============================================
694 /// accessor {set;} for parentNode only visible internally.
696 /// <param name="newParent">new parent node.</param>
697 internal void setParent( XmlNode newParent)
702 //======= Protected methods ==============================================
704 //======= Private Methods ===================================================
706 /// Helper function to perform checks required before insrting a node.
707 /// Throws applicable exceptions on error.
709 /// <param name="newChild"></param>
710 /// <param name="refChild"></param>
711 private void InsertionCheck( XmlNode newChild, XmlNode refChild)
713 if (newChild == null)
714 throw new ArgumentNullException("Null newNode passed to InsertAfter()");
716 if (newChild.Equals(this))
717 throw new ArgumentException("Cannot insert node onto itself");
719 if (NodeType != XmlNodeType.Document && !FOwnerDocument.Equals( newChild.OwnerDocument) )
720 throw new ArgumentException("Reference node has different owner document than this node");
722 XmlDocument ownerDocument = newChild.OwnerDocument;
724 if (ownerDocument.IsReadOnly )
725 throw new ArgumentException("Operation not supported - tree is read-only");
727 //Check that insert node is not in our path to the root
728 XmlNode curParent = _parent;
729 while ( (curParent != null) & (! ownerDocument.Equals(curParent) ))
731 if (curParent.Equals(newChild) )
732 throw new ArgumentException("Cannot insert ancestor a node");
733 curParent = curParent.ParentNode;
738 //===========================================================================
739 //When we're first created, we won't know parent, etc.
740 internal XmlNode( XmlDocument aOwnerDoc )
742 // Don't create childnodes object, since not all derived classes have children
743 FOwnerDocument = aOwnerDoc;
747 } // using namespace System.Xml