// -*- 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)
{
((XmlNodeListAsArrayList)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