// -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- // // System.Xml.XmlNode // // Author: // Daniel Weber (daniel-weber@austin.rr.com) // // (C) 2001 Daniel Weber using System; using System.Collections; using System.Xml.XPath; namespace System.Xml { public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable { //======= Private data members ============================================== private XmlNodeListAsArrayList _childNodes; protected XmlDocument FOwnerDocument; protected XmlNode _parent; // Names of node // for ... // qualified name: foo:bar // namespaceURI = "http://www.foobar.com/schema/foobar" // localName = bar // prefix = foo // Note that namespaces can be nested (child namespace != parent namespace) // namespaces are optional protected string Fname; protected string FnamespaceURI; protected string Fprefix; protected string FlocalName; // baseURI holds the location from which the document was loaded // If the node was created from a document at c:\tmp.xml, then that's what will be here protected string FbaseURI; // value of the node (overriden in classes that do something different) // default behavior is just to store it protected string Fvalue; //===================================================================== // ============ Properties ============================================ //===================================================================== /// /// Get the XmlAttributeCollection representing the attributes /// on the node type. Returns null if the node type is not XmlElement. /// public virtual XmlAttributeCollection Attributes { get { return null; } } /// /// Return the base Uniform Resource Indicator (URI) used to resolve /// this node, or String.Empty. /// public virtual string BaseURI { get { return FbaseURI; } } /// /// Return all child nodes of this node. If there are no children, /// return an empty XmlNodeList; /// public virtual XmlNodeList ChildNodes { get { if (_childNodes == null) _childNodes = new XmlNodeListAsArrayList(); return _childNodes as XmlNodeList; } } /// /// Return first child node as XmlNode or null /// if the node has no children /// public virtual XmlNode FirstChild { get { if (ChildNodes.Count == 0) return null; else return ChildNodes[0]; } } /// /// Return true if the node has children /// public virtual bool HasChildNodes { get { if (ChildNodes.Count == 0) return true; else return false; } } /// /// Get or Set the concatenated values of node and children /// public virtual string InnerText { get { // TODO - implement set InnerText() throw new NotImplementedException(); } set { // TODO - implement set InnerText() throw new NotImplementedException(); } } /// /// Get/Set the XML representing just the child nodes of this node /// public virtual string InnerXml { get { // TODO - implement set InnerXml() throw new NotImplementedException(); } set { // TODO - implement set InnerXml() throw new NotImplementedException(); } } /// /// Property Get - true if node is read-only /// public virtual bool IsReadOnly { get { return OwnerDocument.IsReadOnly; } } /// /// Return the child element named [string]. Returns XmlElement /// Indexer for XmlNode class. /// [System.Runtime.CompilerServices.IndexerNameAttribute("Item")] public virtual XmlElement this [String index] { get { // TODO - implement XmlNode.Item(string) throw new NotImplementedException(); } } /// /// Get the last child node, or null if there are no nodes /// public virtual XmlNode LastChild { get { if (_childNodes.Count == 0) return null; else return _childNodes.Item(_childNodes.Count - 1); } } /// /// Returns the local name of the node with qualifiers removed /// LocalName of ns:elementName = "elementName" /// public abstract string LocalName {get;} /// /// Get the qualified node name /// derived classes must implement as behavior varies /// by tag type. /// public abstract string Name { get; } /// /// Get the namespace URI or String.Empty if none /// public virtual string NamespaceURI { get { // TODO - implement Namespace URI, or determine abstractness throw new NotImplementedException("XmlNode.NamespaceURI not implemented"); } } /// /// Get the node immediatelly following this node, or null /// public virtual XmlNode NextSibling { get { if (_parent != null) { XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList; int ourIndex = children.data.IndexOf(this); return children[ourIndex + 1]; } else return null; } } public virtual XmlNodeType NodeType { get { return XmlNodeType.None; } } /// /// Return the string representing this node and all it's children /// public virtual string OuterXml { get { // TODO - implement OuterXml {get;} throw new NotImplementedException(); } } /// /// Return owning document. /// If this nodeType is a document, return null /// public virtual XmlDocument OwnerDocument { get { return FOwnerDocument; } } /// /// Returns the parent node, or null /// Return value depends on superclass node type /// public virtual XmlNode ParentNode { get { return _parent; } } /// /// set/get the namespace prefix for this node, or /// string.empty if it does not exist /// public virtual string Prefix { get { return Fprefix; } set { // TODO - validation on XmlNode.Prefix {set;}? (no) Fprefix = value; } } /// /// The preceding XmlNode or null /// public virtual XmlNode PreviousSibling { get { if (_parent != null) { XmlNodeListAsArrayList children = _parent.ChildNodes as XmlNodeListAsArrayList; int ourIndex = children.data.IndexOf(this); return children[ourIndex - 1]; } else return null; } } /// /// Get/Set the value for this node /// public virtual string Value { get { return Fvalue; } set { Fvalue = value; } } //===================================================================== //======= Methods ===================================================== //===================================================================== /// /// Appends the specified node to the end of the child node list /// /// /// public virtual XmlNode AppendChild (XmlNode newChild) { _childNodes.Add(newChild); return newChild; } /// /// Return a clone of this node /// /// public virtual object Clone() { // TODO - implement XmlNode.Clone() as object throw new NotImplementedException("object XmlNode.Clone() not implmented"); } /// /// Return a clone of the node /// /// Make copy of all children /// Cloned node public abstract XmlNode CloneNode( bool deep); /// /// Return an XPathNavigator for navigating this node /// /// public System.Xml.XPath.XPathNavigator CreateNavigator() { // TODO - implement CreateNavigator() throw new NotImplementedException(); } /// /// Provide support for "for each" /// /// public IEnumerator GetEnumerator() { return _childNodes.data.GetEnumerator(); } /// /// Look up the closest namespace for this node that is in scope for the given prefix /// /// /// Namespace URI public virtual string GetNamespaceOfPrefix(string prefix) { // TODO - implement GetNamespaceOfPrefix() throw new NotImplementedException(); } /// /// Get the closest xmlns declaration for the given namespace URI that is in scope. /// Returns the prefix defined in that declaration. /// /// /// public virtual string GetPrefixOfNamespace(string namespaceURI) { // TODO - implement GetPrefixOfNamespace throw new NotImplementedException(); } /// /// Insert newChild directly after the reference node. /// If refChild is null, newChild is inserted at the beginning of childnodes. /// If newChild is a document fragment, all nodes are inserted after refChild. /// If newChild is already in the tree, it is first removed. /// /// NewChild was created from differant document. /// RefChild not a child of this node or null. /// Node is read-only /// Node is of type that does not have children /// Node to insert is an ancestor of this node. /// Child node to insert. /// Reference node to insert after /// Removed node, or null if no node removed. public virtual XmlNode InsertAfter(XmlNode newChild, XmlNode refChild) { // Checks parent not ancestor, arguments valid, etc. Throws exception on error InsertionCheck(newChild, refChild); // Scan the node list, looking for refChild and seeing if newChild is in the list // Note that if refNode is null (prepend), we don't want to do the .Equals(null) XmlNode retval = null; int refNodeIndex = -1; for (int i = 0; i < _childNodes.Count; i++) { XmlNode e = _childNodes.data[i] as XmlNode; if (e.Equals(newChild)) { retval = e; FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode); _childNodes.data.RemoveAt(i); newChild.setParent(null); FOwnerDocument.onNodeRemoved(newChild, null); break; } if ( (refChild != null ) & ( e.Equals(refChild) ) ) { refNodeIndex = i; if (retval != null) break; } } if ( ( refNodeIndex == -1 ) & (refChild != null) ) throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()"); FOwnerDocument.onNodeInserting(newChild, this); if (refChild == null) refNodeIndex = 0; else refNodeIndex++; // insert after reference... if (newChild.NodeType == XmlNodeType.DocumentFragment) { // Insert all children, starting from refNodeIndex (0,1,2...n) for (int i = 0; i < newChild.ChildNodes.Count; i++) { XmlNode e = newChild.ChildNodes[i] as XmlNode; FOwnerDocument.onNodeInserting(e, this); _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]); e.setParent(this); FOwnerDocument.onNodeInserted(newChild, this); refNodeIndex ++; } } else { FOwnerDocument.onNodeInserting(newChild, this); _childNodes.data.Insert(refNodeIndex, newChild); newChild.setParent(this); FOwnerDocument.onNodeInserted(newChild, this); } return retval; } /// /// Insert newChild directly before the reference node. /// If refChild is null, newChild is inserted at the end of childnodes. /// If newChild is a document fragment, all nodes are inserted before refChild. /// If newChild is already in the tree, it is first removed. /// /// NewChild was created from different document. /// RefChild not a child of this node, or is null. /// Node is read-only /// Node is of type that does not have children. /// Node to insert is an ancestor of this node. /// Child node to insert. /// Reference node to insert after /// Removed node, or null if no node removed. public virtual XmlNode InsertBefore(XmlNode newChild, XmlNode refChild) { // Checks parent not ancestor, arguments valid, etc. Throws exception on error InsertionCheck(newChild, refChild); // Scan the node list, looking for refChild and seeing if newChild is in the list XmlNode retval = null; int refNodeIndex = -1; for (int i = 0; i < _childNodes.Count; i++) { XmlNode e = _childNodes.data[i] as XmlNode; if (e.Equals(newChild)) { retval = e; FOwnerDocument.onNodeRemoving(newChild, newChild.ParentNode); _childNodes.data.RemoveAt(i); newChild.setParent(null); FOwnerDocument.onNodeRemoved(newChild, null); break; } if ( (refChild != null ) & ( e.Equals(refChild) ) ) { refNodeIndex = i; if (retval != null) break; } } if ( ( refNodeIndex == -1 ) & (refChild != null) ) throw new ArgumentException("Reference node not found (and not null) in call to XmlNode.InsertAfter()"); if (refChild == null) refNodeIndex = _childNodes.Count; if (newChild.NodeType == XmlNodeType.DocumentFragment) { // Insert all children, starting from refNodeIndex (0,1,2...n) for (int i = 0; i < newChild.ChildNodes.Count; i++) { XmlNode e = newChild.ChildNodes[i] as XmlNode; FOwnerDocument.onNodeInserting(e, this); _childNodes.data.Insert(refNodeIndex, newChild.ChildNodes[i]); e.setParent(this); FOwnerDocument.onNodeInserted(newChild, this); refNodeIndex ++; } } else { newChild.OwnerDocument.onNodeInserting(newChild, this); _childNodes.data.Insert(refNodeIndex, newChild); newChild.setParent(this); newChild.OwnerDocument.onNodeInserted(newChild, this); } return retval; } /// /// Put all nodes under this node in "normal" form /// Whatever that means... /// public virtual void Normalize() { // TODO - Implement Normalize() throw new NotImplementedException(); } /// /// Add the specified child to the beginning of the child node list /// /// Node to add /// The node added public virtual XmlNode PrependChild(XmlNode newChild) { return InsertAfter(newChild, null); } /// /// Remove all children and attributes /// public virtual void RemoveAll() { if (_childNodes == null) return; else { // Remove in order, 0..n while (_childNodes.Count > 0) { XmlNode e = _childNodes[0]; FOwnerDocument.onNodeRemoving(e, this); e.setParent(null); _childNodes.data.RemoveAt(0); FOwnerDocument.onNodeRemoved(e, null); } } } /// /// Remove specified child node /// /// /// Removed node public virtual XmlNode RemoveChild(XmlNode oldChild) { // TODO - implement RemoveChild(oldChild) throw new NotImplementedException(); } /// /// Select a list of nodes matching the xpath /// /// /// matching nodes public XmlNodeList SelectNodes( string xpath) { // TODO - imlement SelectNodes(xpath) throw new NotImplementedException(); } /// /// Select a list of nodes matching the xpath. Any prefixes are resolved /// using the passed namespace manager /// /// /// /// public XmlNodeList SelectNodes(string xpath, XmlNamespaceManager nsmgr) { // TODO - implement SelectNodes(xpath, nsmgr) throw new NotImplementedException(); } /// /// Selects the first node that matches xpath /// /// /// public XmlNode SelectSingleNode(string xpatch) { // TODO - implement SelectSingeNode(xpath) throw new NotImplementedException(); } /// /// Returns the first node that matches xpath /// Uses the passed namespace manager to resolve namespace URI's /// /// /// /// public XmlNode SelectSingleNode(string xpath, XmlNamespaceManager nsmgr) { // Implement SelectSingleNode(xpath, nsmgr) throw new NotImplementedException(); } /// /// Tests if the DOM implementation supports the passed feature /// /// /// /// public virtual bool Supports(string feature, string version) { //TODO - implement Supports(feature, version) throw new NotImplementedException(); } /// /// Returns a string representation of the current node and it's children /// /// public override string ToString() { // TODO - implement ToString() throw new NotImplementedException(); } /// /// Saves all children of the current node to the passed writer /// /// public abstract void WriteContentTo(XmlWriter w); /// /// Saves the current node to writer w /// /// public abstract void WriteTo(XmlWriter w); //======= Internal methods =============================================== /// /// accessor {set;} for parentNode only visible internally. /// /// new parent node. internal void setParent( XmlNode newParent) { _parent = newParent; } //======= Protected methods ============================================== //======= Private Methods =================================================== /// /// Helper function to perform checks required before insrting a node. /// Throws applicable exceptions on error. /// /// /// private void InsertionCheck( XmlNode newChild, XmlNode refChild) { if (newChild == null) throw new ArgumentNullException("Null newNode passed to InsertAfter()"); if (newChild.Equals(this)) throw new ArgumentException("Cannot insert node onto itself"); if (NodeType != XmlNodeType.Document && !FOwnerDocument.Equals( newChild.OwnerDocument) ) throw new ArgumentException("Reference node has different owner document than this node"); XmlDocument ownerDocument = newChild.OwnerDocument; if (ownerDocument.IsReadOnly ) throw new ArgumentException("Operation not supported - tree is read-only"); //Check that insert node is not in our path to the root XmlNode curParent = _parent; while ( (curParent != null) & (! ownerDocument.Equals(curParent) )) { if (curParent.Equals(newChild) ) throw new ArgumentException("Cannot insert ancestor a node"); curParent = curParent.ParentNode; } } // Constructors //=========================================================================== //When we're first created, we won't know parent, etc. internal XmlNode( XmlDocument aOwnerDoc ) { // Don't create childnodes object, since not all derived classes have children FOwnerDocument = aOwnerDoc; } } // XmlNode } // using namespace System.Xml