1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
\r
3 // System.Xml.XmlNode
\r
6 // Daniel Weber (daniel-weber@austin.rr.com)
\r
8 // (C) 2001 Daniel Weber
\r
11 using System.Collections;
\r
12 using System.Xml.XPath;
\r
14 namespace System.Xml
\r
16 public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable
\r
18 //======= Private data members ==============================================
\r
19 private XmlNodeListAsArrayList _childNodes;
\r
20 protected XmlDocument FOwnerDocument;
\r
21 protected XmlNode _parent;
\r
24 /// Return a clone of this node
\r
26 /// <returns></returns>
\r
27 public virtual object Clone()
\r
29 // TODO - implement XmlNode.Clone() as object
\r
30 throw new NotImplementedException("object XmlNode.Clone() not implmented");
\r
33 // ============ Properties ============================
\r
35 /// Get the XmlAttributeCollection representing the attributes
\r
36 /// on the node type. Returns null if the node type is not XmlElement.
\r
38 public virtual XmlAttributeCollection Attributes
\r
46 /// Return the base Uniform Resource Indicator (URI) used to resolve
\r
47 /// this node, or String.Empty.
\r
49 public virtual string BaseURI
\r
53 // TODO - implement XmlNode.BaseURI {get;}
\r
54 throw new NotImplementedException("XmlNode.BaseURI not implemented");
\r
60 /// Return all child nodes of this node. If there are no children,
\r
61 /// return an empty XmlNodeList;
\r
63 public virtual XmlNodeList ChildNodes
\r
67 if (_childNodes == null)
\r
68 _childNodes = new XmlNodeListAsArrayList();
\r
70 return _childNodes as XmlNodeList;
\r
75 /// Return first child node as XmlNode or null
\r
76 /// if the node has no children
\r
78 public virtual XmlNode FirstChild
\r
82 if (ChildNodes.Count == 0)
\r
85 return ChildNodes[0];
\r
90 /// Return true if the node has children
\r
92 public virtual bool HasChildNodes
\r
96 if (_childNodes.Count == 0)
\r
104 /// Get or Set the concatenated values of node and children
\r
106 public virtual string InnerText
\r
110 // TODO - implement set InnerText()
\r
111 throw new NotImplementedException();
\r
116 // TODO - implement set InnerText()
\r
117 throw new NotImplementedException();
\r
122 /// Get/Set the XML representing just the child nodes of this node
\r
124 public virtual string InnerXml
\r
128 // TODO - implement set InnerXml()
\r
129 throw new NotImplementedException();
\r
134 // TODO - implement set InnerXml()
\r
135 throw new NotImplementedException();
\r
140 /// Property Get - true if node is read-only
\r
142 public virtual bool IsReadOnly
\r
146 return OwnerDocument.IsReadOnly;
\r
151 /// Return the child element named [string]. Returns XmlElement
\r
152 /// Indexer for XmlNode class.
\r
154 [System.Runtime.CompilerServices.CSharp.IndexerName("Item")]
\r
155 public virtual XmlElement this [String index]
\r
159 // TODO - implement XmlNode.Item(string)
\r
160 throw new NotImplementedException();
\r
165 /// Get the last child node, or null if there are no nodes
\r
167 public virtual XmlNode LastChild
\r
171 if (_childNodes.Count == 0)
\r
174 return _childNodes.Item(_childNodes.Count - 1);
\r
180 /// Returns the local name of the node with qualifiers removed
\r
181 /// LocalName of ns:elementName = "elementName"
\r
183 public abstract string LocalName {get;}
\r
186 /// Get the qualified node name
\r
187 /// derived classes must implement as behavior varies
\r
190 public abstract string Name { get; }
\r
193 /// Get the namespace URI or String.Empty if none
\r
195 public virtual string NamespaceURI
\r
199 // TODO - implement Namespace URI, or determine abstractness
\r
200 throw new NotImplementedException("XmlNode.NamespaceURI not implemented");
\r
205 /// Get the node immediatelly following this node, or null
\r
207 public virtual XmlNode NextSibling
\r
212 if (_parent != null)
\r
214 XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList;
\r
215 int ourIndex = children.data.IndexOf(this);
\r
216 return children[ourIndex + 1];
\r
223 public virtual XmlNodeType NodeType
\r
227 return XmlNodeType.None;
\r
232 /// Return the string representing this node and all it's children
\r
234 public virtual string OuterXml
\r
238 // TODO - implement OuterXml {get;}
\r
239 throw new NotImplementedException();
\r
244 /// Return owning document.
\r
245 /// If this nodeType is a document, return null
\r
247 public virtual XmlDocument OwnerDocument
\r
251 return FOwnerDocument;
\r
256 /// Returns the parent node, or null
\r
257 /// Return value depends on superclass node type
\r
259 public virtual XmlNode ParentNode
\r
268 /// set/get the namespace prefix for this node, or
\r
269 /// string.empty if it does not exist
\r
271 public virtual string Prefix
\r
275 // TODO - implement Prefix {get;}
\r
276 throw new NotImplementedException();
\r
281 // TODO - implement Prefix {set;}
\r
282 throw new NotImplementedException();
\r
287 /// The preceding XmlNode or null
\r
289 public virtual XmlNode PreviousSibling {
\r
292 if (_parent != null)
\r
294 XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList;
\r
295 int ourIndex = children.data.IndexOf(this);
\r
296 return children[ourIndex - 1];
\r
304 /// Get/Set the value for this node
\r
306 public virtual string Value
\r
310 // TODO - implement Value {get;}
\r
311 throw new NotImplementedException();
\r
316 // TODO - implement Value {set;}
\r
317 throw new NotImplementedException();
\r
321 //======= Methods ==========================
\r
323 /// Appends the specified node to the end of the child node list
\r
325 /// <param name="newChild"></param>
\r
326 /// <returns></returns>
\r
327 public virtual XmlNode AppendChild (XmlNode newChild)
\r
329 return InsertBefore(newChild, null);
\r
333 /// Return a clone of the node
\r
335 /// <param name="deep">Make copy of all children</param>
\r
336 /// <returns>Cloned node</returns>
\r
337 public abstract XmlNode CloneNode( bool deep);
\r
340 /// Return an XPathNavigator for navigating this node
\r
342 /// <returns></returns>
\r
343 public System.Xml.XPath.XPathNavigator CreateNavigator()
\r
345 // TODO - implement CreateNavigator()
\r
346 throw new NotImplementedException();
\r
350 /// Provide support for "for each"
\r
352 /// <returns></returns>
\r
353 public IEnumerator GetEnumerator()
\r
355 return _childNodes.data.GetEnumerator();
\r
359 /// Look up the closest namespace for this node that is in scope for the given prefix
\r
361 /// <param name="prefix"></param>
\r
362 /// <returns>Namespace URI</returns>
\r
363 public virtual string GetNamespaceOfPrefix(string prefix)
\r
365 // TODO - implement GetNamespaceOfPrefix()
\r
366 throw new NotImplementedException();
\r
370 /// Get the closest xmlns declaration for the given namespace URI that is in scope.
\r
371 /// Returns the prefix defined in that declaration.
\r
373 /// <param name="namespaceURI"></param>
\r
374 /// <returns></returns>
\r
375 public virtual string GetPrefixOfNamespace(string namespaceURI)
\r
377 // TODO - implement GetPrefixOfNamespace
\r
378 throw new NotImplementedException();
\r
382 /// Insert newChild directly after the reference node.
\r
383 /// If refChild is null, newChild is inserted at the beginning of childnodes.
\r
384 /// If newChild is a document fragment, all nodes are inserted after refChild.
\r
385 /// If newChild is already in the tree, it is first removed.
\r
387 /// <exception cref="ArgumentException">NewChild was created from differant document.
\r
388 /// RefChild not a child of this node or null.
\r
389 /// Node is read-only</exception>
\r
390 /// <exception cref="InvalidOperationException">Node is of type that does not have children
\r
391 /// Node to insert is an ancestor of this node.</exception>
\r
392 /// <param name="newChild">Child node to insert.</param>
\r
393 /// <param name="refChild">Reference node to insert after</param>
\r
394 /// <returns></returns>
\r
395 public virtual XmlNode InsertAfter(XmlNode newChild, XmlNode refChild)
\r
397 // Checks parent not ancestor, arguments valid, etc. Throws exception on error
\r
398 InsertionCheck(newChild, refChild);
\r
400 // Scan the node list, looking for refChild and seeing if newChild is in the list
\r
401 // Note that if refNode is null (prepend), we don't want to do the .Equals(null)
\r
402 XmlNode retval = null;
\r
403 int refNodeIndex = -1;
\r
405 for (int i = 0; i < _childNodes.Count; i++)
\r
407 XmlNode e = _childNodes.data[i] as XmlNode;
\r
408 if (e.Equals(newChild))
\r
411 FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode);
\r
412 _childNodes.data.RemoveAt(i);
\r
413 newChild.setParent(null);
\r
414 FOwnerDocument.onNodeRemoved(newChild, null);
\r
419 if ( (refChild != null ) & ( e.Equals(refChild) ) )
\r
423 if (retval != null)
\r
428 if ( ( refNodeIndex == -1 ) & (refChild != null) )
\r
429 throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()");
\r
431 FOwnerDocument.onNodeInserting(newChild, this);
\r
433 if (refChild == null)
\r
436 refNodeIndex++; // insert after reference...
\r
438 if (newChild.NodeType == XmlNodeType.DocumentFragment)
\r
440 // Insert all children, starting from refNodeIndex (0,1,2...n)
\r
441 for (int i = 0; i < newChild.ChildNodes.Count; i++)
\r
443 XmlNode e = newChild.ChildNodes[i] as XmlNode;
\r
444 FOwnerDocument.onNodeInserting(e, this);
\r
445 _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]);
\r
447 FOwnerDocument.onNodeInserted(newChild, this);
\r
453 FOwnerDocument.onNodeInserting(newChild, this);
\r
454 _childNodes.data.Insert(refNodeIndex, newChild);
\r
455 newChild.setParent(this);
\r
456 FOwnerDocument.onNodeInserted(newChild, this);
\r
463 /// Insert newChild directly before the reference node.
\r
465 /// <param name="newChild"></param>
\r
466 /// <param name="refChild"></param>
\r
467 /// <returns></returns>
\r
468 public virtual XmlNode InsertBefore(XmlNode newChild, XmlNode refChild)
\r
470 // Checks parent not ancestor, arguments valid, etc. Throws exception on error
\r
471 InsertionCheck(newChild, refChild);
\r
473 // Scan the node list, looking for refChild and seeing if newChild is in the list
\r
474 XmlNode retval = null;
\r
475 int refNodeIndex = -1;
\r
477 for (int i = 0; i < _childNodes.Count; i++)
\r
479 XmlNode e = _childNodes.data[i] as XmlNode;
\r
480 if (e.Equals(newChild))
\r
483 FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode);
\r
484 _childNodes.data.RemoveAt(i);
\r
485 newChild.setParent(null);
\r
486 FOwnerDocument.onNodeRemoved(newChild, null);
\r
490 if ( (refChild != null ) & ( e.Equals(refChild) ) )
\r
494 if (retval != null)
\r
499 if ( ( refNodeIndex == -1 ) & (refChild != null) )
\r
500 throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()");
\r
504 if (refChild == null)
\r
505 refNodeIndex = _childNodes.Count;
\r
507 if (newChild.NodeType == XmlNodeType.DocumentFragment)
\r
509 // Insert all children, starting from refNodeIndex (0,1,2...n)
\r
510 for (int i = 0; i < newChild.ChildNodes.Count; i++)
\r
512 XmlNode e = newChild.ChildNodes[i] as XmlNode;
\r
513 FOwnerDocument.onNodeInserting(e, this);
\r
514 _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]);
\r
516 FOwnerDocument.onNodeInserted(newChild, this);
\r
522 FOwnerDocument.onNodeInserting(newChild, this);
\r
523 _childNodes.data.Insert(refNodeIndex, newChild);
\r
524 newChild.setParent(this);
\r
525 FOwnerDocument.onNodeInserted(newChild, this);
\r
533 /// Put all nodes under this node in "normal" form
\r
534 /// Whatever that means...
\r
536 public virtual void Normalize()
\r
538 // TODO - Implement Normalize()
\r
539 throw new NotImplementedException();
\r
543 /// Add the specified child to the beginning of the child node list
\r
545 /// <param name="newChild">Node to add</param>
\r
546 /// <returns>The node added</returns>
\r
547 public virtual XmlNode PrependChild(XmlNode newChild)
\r
549 return InsertAfter(newChild, null);
\r
553 /// Remove all children and attributes
\r
555 public virtual void RemoveAll()
\r
557 if (_childNodes == null)
\r
561 // Remove in order, 0..n
\r
562 while (_childNodes.Count > 0)
\r
564 XmlNode e = _childNodes[0];
\r
565 FOwnerDocument.onNodeRemoving(e, this);
\r
567 _childNodes.data.RemoveAt(0);
\r
568 FOwnerDocument.onNodeRemoved(e, null);
\r
574 /// Remove specified child node
\r
576 /// <param name="oldChild"></param>
\r
577 /// <returns>Removed node</returns>
\r
578 public virtual XmlNode RemoveChild(XmlNode oldChild)
\r
580 // TODO - implement RemoveChild(oldChild)
\r
581 throw new NotImplementedException();
\r
585 /// Select a list of nodes matching the xpath
\r
587 /// <param name="xpath"></param>
\r
588 /// <returns>matching nodes</returns>
\r
589 public XmlNodeList SelectNodes( string xpath)
\r
591 // TODO - imlement SelectNodes(xpath)
\r
592 throw new NotImplementedException();
\r
596 /// Select a list of nodes matching the xpath. Any prefixes are resolved
\r
597 /// using the passed namespace manager
\r
599 /// <param name="xpath"></param>
\r
600 /// <param name="nsmgr"></param>
\r
601 /// <returns></returns>
\r
602 public XmlNodeList SelectNodes(string xpath, XmlNamespaceManager nsmgr)
\r
604 // TODO - implement SelectNodes(xpath, nsmgr)
\r
605 throw new NotImplementedException();
\r
609 /// Selects the first node that matches xpath
\r
611 /// <param name="?"></param>
\r
612 /// <returns></returns>
\r
613 public XmlNode SelectSingleNode(string xpatch)
\r
615 // TODO - implement SelectSingeNode(xpath)
\r
616 throw new NotImplementedException();
\r
620 /// Returns the first node that matches xpath
\r
621 /// Uses the passed namespace manager to resolve namespace URI's
\r
623 /// <param name="xpath"></param>
\r
624 /// <param name="nsmgr"></param>
\r
625 /// <returns></returns>
\r
626 public XmlNode SelectSingleNode(string xpath, XmlNamespaceManager nsmgr)
\r
628 // Implement SelectSingleNode(xpath, nsmgr)
\r
629 throw new NotImplementedException();
\r
633 /// Tests if the DOM implementation supports the passed feature
\r
635 /// <param name="feature"></param>
\r
636 /// <param name="version"></param>
\r
637 /// <returns></returns>
\r
638 public virtual bool Supports(string feature, string version)
\r
640 //TODO - implement Supports(feature, version)
\r
641 throw new NotImplementedException();
\r
645 /// Returns a string representation of the current node and it's children
\r
647 /// <returns></returns>
\r
648 public override string ToString()
\r
650 // TODO - implement ToString()
\r
651 throw new NotImplementedException();
\r
655 /// Saves all children of the current node to the passed writer
\r
657 /// <param name="w"></param>
\r
658 public abstract void WriteContentTo(XmlWriter w);
\r
661 /// Saves the current node to writer w
\r
663 /// <param name="w"></param>
\r
664 public abstract void WriteTo(XmlWriter w);
\r
666 //======= Internal methods ===============================================
\r
668 /// accessor {set;} for parentNode only visible internally.
\r
670 /// <param name="newParent">new parent node.</param>
\r
671 internal void setParent( XmlNode newParent)
\r
673 if (newParent.OwnerDocument.Equals( FOwnerDocument) )
\r
674 _parent = newParent;
\r
676 throw new ArgumentException("New parent node owner does not match");
\r
680 //======= Protected methods ==============================================
\r
682 //======= Private Methods ===================================================
\r
684 /// Helper function to perform checks required before insrting a node.
\r
685 /// Throws applicable exceptions on error.
\r
687 /// <param name="newChild"></param>
\r
688 /// <param name="refChild"></param>
\r
689 private void InsertionCheck( XmlNode newChild, XmlNode refChild)
\r
691 if (newChild == null)
\r
692 throw new ArgumentNullException("Null newNode passed to InsertAfter()");
\r
694 if (newChild.Equals(this))
\r
695 throw new ArgumentException("Cannot insert node onto itself");
\r
697 if (! FOwnerDocument.Equals( newChild.OwnerDocument) )
\r
698 throw new ArgumentException("Reference node has different owner document than this node");
\r
700 if ( FOwnerDocument.IsReadOnly )
\r
701 throw new ArgumentException("Operation not supported - tree is read-only");
\r
703 //Check that insert node is not in our path to the root
\r
704 XmlNode curParent = _parent;
\r
705 while ( (curParent != null) & (! FOwnerDocument.Equals(curParent) ))
\r
707 if (curParent.Equals(newChild) )
\r
708 throw new ArgumentException("Cannot insert ancestor a node");
\r
709 curParent = curParent.ParentNode;
\r
715 //===========================================================================
\r
716 //When we're first created, we won't know parent, etc.
\r
717 internal XmlNode( XmlDocument aOwnerDoc )
\r
719 // Don't create childnodes object, since not all derived classes have children
\r
720 FOwnerDocument = aOwnerDoc;
\r
724 } // using namespace System.Xml
\r