XmlDocument ownerDocument;
XmlNode parentNode;
StringBuilder tmpBuilder;
+ XmlLinkedNode lastLinkedChild;
#endregion
}
internal virtual XmlLinkedNode LastLinkedChild {
- get { return null; }
- set { }
+ get { return lastLinkedChild; }
+ set { lastLinkedChild = value; }
}
public abstract string LocalName { get; }
break;
}
- while (node.NodeType != XmlNodeType.Document) {
+ while (node != null && node.Attributes != null) {
foreach (XmlAttribute attr in node.Attributes) {
if (attr.Prefix == "xmlns" && attr.Value == namespaceURI)
return attr.LocalName;
public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
{
- // I assume that insertAfter(n1, n2) equals to InsertBefore(n1, n2.PreviousSibling).
+ // InsertAfter(n1, n2) is equivalent to InsertBefore(n1, n2.PreviousSibling).
// I took this way because current implementation
- // Calling InsertAfter() from InsertBefore() is
- // subsequently to use 'NextSibling' which is
- // faster than 'PreviousSibling' (these children are
- // forward-only linked list).
+ // Calling InsertBefore() in this method is faster than
+ // the counterpart, since NextSibling is faster than
+ // PreviousSibling (these children are forward-only list).
XmlNode argNode = null;
- if(refChild != null)
+ if (refChild != null)
argNode = refChild.NextSibling;
- else if(ChildNodes.Count > 0)
+ else if (ChildNodes.Count > 0)
argNode = FirstChild;
return InsertBefore (newChild, argNode);
}
public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
+ {
+ return InsertBefore (newChild, refChild, true, true);
+ }
+
+ // check for the node to be one of node ancestors
+ internal bool IsAncestor (XmlNode newChild)
+ {
+ XmlNode currNode = this.ParentNode;
+ while(currNode != null)
+ {
+ if(currNode == newChild)
+ return true;
+ currNode = currNode.ParentNode;
+ }
+ return false;
+ }
+
+ internal XmlNode InsertBefore (XmlNode newChild, XmlNode refChild, bool checkNodeType, bool raiseEvent)
+ {
+ if (checkNodeType)
+ CheckNodeInsertion (newChild, refChild);
+
+ XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument) this : OwnerDocument;
+
+ if (raiseEvent)
+ ownerDoc.onNodeInserting (newChild, this);
+
+ if (newChild.ParentNode != null)
+ newChild.ParentNode.RemoveChild (newChild, checkNodeType);
+
+ if (newChild.NodeType == XmlNodeType.DocumentFragment) {
+ int x = newChild.ChildNodes.Count;
+ for (int i = 0; i < x; i++) {
+ XmlNode n = newChild.ChildNodes [0];
+ this.InsertBefore (n, refChild); // recursively invokes events. (It is compatible with MS implementation.)
+ }
+ }
+ else {
+ XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
+ XmlLinkedNode lastLinkedChild = LastLinkedChild;
+
+ newLinkedChild.parentNode = this;
+
+ if (refChild == null) {
+ // newChild is the last child:
+ // * set newChild as NextSibling of the existing lastchild
+ // * set LastChild = newChild
+ // * set NextSibling of newChild as FirstChild
+ if (LastLinkedChild != null) {
+ XmlLinkedNode formerFirst = (XmlLinkedNode) FirstChild;
+ LastLinkedChild.NextLinkedSibling = newLinkedChild;
+ LastLinkedChild = newLinkedChild;
+ newLinkedChild.NextLinkedSibling = formerFirst;
+ } else {
+ LastLinkedChild = newLinkedChild;
+ LastLinkedChild.NextLinkedSibling = newLinkedChild; // FirstChild
+ }
+ } else {
+ // newChild is not the last child:
+ // * if newchild is first, then set next of lastchild is newChild.
+ // otherwise, set next of previous sibling to newChild
+ // * set next of newChild to refChild
+ XmlLinkedNode prev = refChild.PreviousSibling as XmlLinkedNode;
+ if (prev == null)
+ LastLinkedChild.NextLinkedSibling = newLinkedChild;
+ else
+ prev.NextLinkedSibling = newLinkedChild;
+ newLinkedChild.NextLinkedSibling = refChild as XmlLinkedNode;
+ }
+ switch (newChild.NodeType) {
+ case XmlNodeType.EntityReference:
+ ((XmlEntityReference) newChild).SetReferencedEntityContent ();
+ break;
+ case XmlNodeType.Entity:
+ ((XmlEntity) newChild).SetEntityContent ();
+ break;
+ case XmlNodeType.DocumentType:
+ foreach (XmlEntity ent in ((XmlDocumentType)newChild).Entities)
+ ent.SetEntityContent ();
+ break;
+ }
+
+ if (raiseEvent)
+ ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
+ }
+ return newChild;
+ }
+
+ private void CheckNodeInsertion (XmlNode newChild, XmlNode refChild)
{
XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
throw new ArgumentException ("argument nodes are on the different documents.");
- // This check is done by MS.NET 1.0, but isn't done for MS.NET 1.1.
- // Skip this check in the meantime...
-// if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
-// throw new XmlException ("multiple document element not allowed.");
+ if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
+ throw new XmlException ("multiple document element not allowed.");
// checking validity finished. then appending...
- return insertBeforeIntern (newChild, refChild);
- }
-
- internal XmlNode insertBeforeIntern (XmlNode newChild, XmlNode refChild)
- {
- XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+
+ if (newChild == this || IsAncestor (newChild))
+ throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
- ownerDoc.onNodeInserting (newChild, this);
-
- if(newChild.ParentNode != null)
- newChild.ParentNode.RemoveChild (newChild);
-
- if(newChild.NodeType == XmlNodeType.DocumentFragment) {
- int x = newChild.ChildNodes.Count;
- for(int i=0; i<x; i++) {
- XmlNode n = newChild.ChildNodes [0];
- this.InsertBefore (n, refChild); // recursively invokes events. (It is compatible with MS implementation.)
- }
- }
- else {
- XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
- XmlLinkedNode lastLinkedChild = LastLinkedChild;
-
- newLinkedChild.parentNode = this;
-
- if(refChild == null) {
- // append last, so:
- // * set nextSibling of previous lastchild to newChild
- // * set lastchild = newChild
- // * set next of newChild to firstChild
- if(LastLinkedChild != null) {
- XmlLinkedNode formerFirst = FirstChild as XmlLinkedNode;
- LastLinkedChild.NextLinkedSibling = newLinkedChild;
- LastLinkedChild = newLinkedChild;
- newLinkedChild.NextLinkedSibling = formerFirst;
- }
- else {
- LastLinkedChild = newLinkedChild;
- LastLinkedChild.NextLinkedSibling = newLinkedChild; // FirstChild
- }
- }
- else {
- // append not last, so:
- // * if newchild is first, then set next of lastchild is newChild.
- // otherwise, set next of previous sibling to newChild
- // * set next of newChild to refChild
- XmlLinkedNode prev = refChild.PreviousSibling as XmlLinkedNode;
- if(prev == null)
- LastLinkedChild.NextLinkedSibling = newLinkedChild;
- else
- prev.NextLinkedSibling = newLinkedChild;
- newLinkedChild.NextLinkedSibling = refChild as XmlLinkedNode;
- }
- ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
- }
- return newChild;
}
public virtual void Normalize ()
public virtual XmlNode RemoveChild (XmlNode oldChild)
{
- XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
- if(oldChild.ParentNode != this)
- throw new XmlException ("specified child is not child of this node.");
-
- ownerDoc.onNodeRemoving (oldChild, oldChild.ParentNode);
+ return RemoveChild (oldChild, true);
+ }
+ private void CheckNodeRemoval ()
+ {
if (NodeType != XmlNodeType.Attribute &&
NodeType != XmlNodeType.Element &&
NodeType != XmlNodeType.Document &&
if (IsReadOnly)
throw new ArgumentException (String.Format ("This {0} node is read only.", NodeType));
+ }
+
+ internal XmlNode RemoveChild (XmlNode oldChild, bool checkNodeType)
+ {
+ XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+ if(oldChild.ParentNode != this)
+ throw new XmlException ("specified child is not child of this node.");
+
+ if (checkNodeType)
+ ownerDoc.onNodeRemoving (oldChild, oldChild.ParentNode);
+
+ if (checkNodeType)
+ CheckNodeRemoval ();
if (Object.ReferenceEquals (LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals (LastLinkedChild, oldChild))
// If there is only one children, simply clear.
oldLinkedChild.NextLinkedSibling = null;
}
- ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
+ if (checkNodeType)
+ ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
oldChild.parentNode = null; // clear parent 'after' above logic.
return oldChild;
{
if(oldChild.ParentNode != this)
throw new InvalidOperationException ("oldChild is not a child of this node.");
- XmlNode parent = this.ParentNode;
- while(parent != null) {
- if(newChild == parent)
- throw new InvalidOperationException ("newChild is ancestor of this node.");
- parent = parent.ParentNode;
- }
+
+ if (newChild == this || IsAncestor (newChild))
+ throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
+
foreach(XmlNode n in ChildNodes) {
if(n == oldChild) {
XmlNode prev = oldChild.PreviousSibling;
return oldChild;
}
+ internal void SearchDescendantElements (string name, bool matchAll, ArrayList list)
+ {
+ foreach (XmlNode n in ChildNodes){
+ if (n.NodeType != XmlNodeType.Element)
+ continue;
+ if (matchAll || n.Name == name)
+ list.Add (n);
+ n.SearchDescendantElements (name, matchAll, list);
+ }
+ }
+
+ internal void SearchDescendantElements (string name, bool matchAllName, string ns, bool matchAllNS, ArrayList list)
+ {
+ foreach (XmlNode n in ChildNodes){
+ if (n.NodeType != XmlNodeType.Element)
+ continue;
+ if ((matchAllName || n.LocalName == name)
+ && (matchAllNS || n.NamespaceURI == ns))
+ list.Add (n);
+ n.SearchDescendantElements (name, matchAllName, ns, matchAllNS, list);
+ }
+ }
+
public XmlNodeList SelectNodes (string xpath)
{
return SelectNodes (xpath, null);
}
- [MonoTODO ("return nodes in document order")]
public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
{
XPathNavigator nav = CreateNavigator ();
return SelectSingleNode (xpath, null);
}
- [MonoTODO ("return node in document order")]
public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
{
XPathNavigator nav = CreateNavigator ();
// It parses this and all the ancestor elements,
// find 'xmlns' declarations, stores and then return them.
- // TODO: tests
internal XmlNamespaceManager ConstructNamespaceManager ()
{
XmlDocument doc = this is XmlDocument ? (XmlDocument)this : this.OwnerDocument;