XmlDocument ownerDocument;
XmlNode parentNode;
+ StringBuilder tmpBuilder;
+ XmlLinkedNode lastLinkedChild;
#endregion
get { return LastChild != null; }
}
- [MonoTODO("confirm whether this way is right for each not-overriden types.")]
public virtual string InnerText {
get {
StringBuilder builder = new StringBuilder ();
return builder.ToString ();
}
- set { throw new NotImplementedException (); }
+ set { throw new InvalidOperationException ("This node is read only. Cannot be modified."); }
}
private void AppendChildValues (XmlNode parent, StringBuilder builder)
XmlNode node = parent.FirstChild;
while (node != null) {
- if (node.NodeType == XmlNodeType.Text)
- builder.Append (node.Value);
+ switch (node.NodeType) {
+ case XmlNodeType.Text:
+ case XmlNodeType.CDATA:
+ case XmlNodeType.SignificantWhitespace:
+ case XmlNodeType.Whitespace:
+ builder.Append (node.Value);
+ break;
+ }
AppendChildValues (node, builder);
node = node.NextSibling;
}
}
internal virtual XmlLinkedNode LastLinkedChild {
- get { return null; }
- set { }
+ get { return lastLinkedChild; }
+ set { lastLinkedChild = value; }
}
public abstract string LocalName { get; }
internal virtual XPathNodeType XPathNodeType {
get {
- throw new InvalidOperationException ();
+ throw new InvalidOperationException ("Can not get XPath node type from " + this.GetType ().ToString ());
}
}
public abstract XmlNode CloneNode (bool deep);
- [MonoTODO]
public XPathNavigator CreateNavigator ()
{
XmlDocument document = this.NodeType == XmlNodeType.Document ?
return new XmlNodeListChildren (this).GetEnumerator ();
}
- [MonoTODO("performance problem.")]
public virtual string GetNamespaceOfPrefix (string prefix)
{
- XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
- return nsmgr.LookupNamespace (prefix);
+ if (prefix == null)
+ throw new ArgumentNullException ("prefix");
+
+ XmlNode node;
+ switch (NodeType) {
+ case XmlNodeType.Attribute:
+ node = ((XmlAttribute) this).OwnerElement;
+ if (node == null)
+ return String.Empty;
+ break;
+ case XmlNodeType.Element:
+ node = this;
+ break;
+ default:
+ node = ParentNode;
+ break;
+ }
+
+ while (node != null) {
+ if (node.Prefix == prefix)
+ return node.NamespaceURI;
+ if (node.Attributes != null) {
+ int count = node.Attributes.Count;
+ for (int i = 0; i < count; i++) {
+ XmlAttribute attr = node.Attributes [i];
+ if (prefix == attr.LocalName && attr.Prefix == "xmlns"
+ || attr.Name == "xmlns" && prefix == String.Empty)
+ return attr.Value;
+ }
+ }
+ node = node.ParentNode;
+ }
+ return String.Empty;
}
- [MonoTODO("performance problem.")]
public virtual string GetPrefixOfNamespace (string namespaceURI)
{
- XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
- string ns = nsmgr.LookupPrefix (namespaceURI);
- return (ns != null) ? ns : String.Empty;
+ XmlNode node;
+ switch (NodeType) {
+ case XmlNodeType.Attribute:
+ node = ((XmlAttribute) this).OwnerElement;
+ break;
+ case XmlNodeType.Element:
+ node = this;
+ break;
+ default:
+ node = ParentNode;
+ break;
+ }
+
+ while (node != null && node.Attributes != null) {
+ foreach (XmlAttribute attr in node.Attributes) {
+ if (attr.Prefix == "xmlns" && attr.Value == namespaceURI)
+ return attr.LocalName;
+ else if (attr.Name == "xmlns" && attr.Value == namespaceURI)
+ return String.Empty;
+ }
+ node = node.ParentNode;
+ }
+ return String.Empty;
}
object ICloneable.Clone ()
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);
}
- [MonoTODO("If inserted node is entity reference, then check conforming entity. Wait for DTD implementation.")]
public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
{
- XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
-
- if (NodeType == XmlNodeType.Document ||
- NodeType == XmlNodeType.Element ||
- NodeType == XmlNodeType.Attribute ||
- NodeType == XmlNodeType.DocumentFragment) {
- if (IsReadOnly)
- throw new ArgumentException ("The specified node is readonly.");
-
- if (newChild.OwnerDocument != ownerDoc)
- throw new ArgumentException ("Can't append a node created by another document.");
-
- 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.");
-
- // checking validity finished. then appending...
+ return InsertBefore (newChild, refChild, true, true);
+ }
- return insertBeforeIntern (newChild, refChild);
- }
- else
- throw new InvalidOperationException (String.Format ("current node {0} is not allowed to have any children.", NodeType));
+ // 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 insertBeforeIntern (XmlNode newChild, XmlNode refChild)
+ internal XmlNode InsertBefore (XmlNode newChild, XmlNode refChild, bool checkNodeType, bool raiseEvent)
{
- XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+ if (checkNodeType)
+ CheckNodeInsertion (newChild, refChild);
+
+ XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument) this : OwnerDocument;
- ownerDoc.onNodeInserting (newChild, this);
+ if (raiseEvent)
+ ownerDoc.onNodeInserting (newChild, this);
- if(newChild.ParentNode != null)
- newChild.ParentNode.RemoveChild (newChild);
+ if (newChild.ParentNode != null)
+ newChild.ParentNode.RemoveChild (newChild, checkNodeType);
- if(newChild.NodeType == XmlNodeType.DocumentFragment) {
+ if (newChild.NodeType == XmlNodeType.DocumentFragment) {
int x = newChild.ChildNodes.Count;
- for(int i=0; i<x; i++) {
+ 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.)
}
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;
+ 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 {
+ } else {
LastLinkedChild = newLinkedChild;
LastLinkedChild.NextLinkedSibling = newLinkedChild; // FirstChild
}
- }
- else {
- // append not last, so:
+ } 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)
+ if (prev == null)
LastLinkedChild.NextLinkedSibling = newLinkedChild;
else
prev.NextLinkedSibling = newLinkedChild;
newLinkedChild.NextLinkedSibling = refChild as XmlLinkedNode;
}
- ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
+ 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;
}
- [MonoTODO]
+ private void CheckNodeInsertion (XmlNode newChild, XmlNode refChild)
+ {
+ XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+
+ if (NodeType != XmlNodeType.Element &&
+ NodeType != XmlNodeType.Attribute &&
+ NodeType != XmlNodeType.Document &&
+ NodeType != XmlNodeType.DocumentFragment)
+ throw new InvalidOperationException (String.Format ("current node {0} is not allowed to have any children.", NodeType));
+
+ switch (NodeType) {
+ case XmlNodeType.Attribute:
+ switch (newChild.NodeType) {
+ case XmlNodeType.Text:
+ case XmlNodeType.EntityReference:
+ break;
+ default:
+ throw new ArgumentException (String.Format (
+ "Cannot insert specified type of node {0} as a child of this node {0}.",
+ newChild.NodeType, NodeType));
+ }
+ break;
+ case XmlNodeType.Element:
+ switch (newChild.NodeType) {
+ case XmlNodeType.Attribute:
+ case XmlNodeType.Document:
+ case XmlNodeType.DocumentType:
+ case XmlNodeType.Entity:
+ case XmlNodeType.Notation:
+ case XmlNodeType.XmlDeclaration:
+ throw new ArgumentException ("Cannot insert specified type of node as a child of this node.");
+ }
+ break;
+ }
+
+ if (IsReadOnly)
+ throw new ArgumentException ("The specified node is readonly.");
+
+ if (newChild.OwnerDocument != ownerDoc)
+ throw new ArgumentException ("Can't append a node created by another document.");
+
+ if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
+ throw new ArgumentException ("argument nodes are on the different documents.");
+
+ if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
+ throw new XmlException ("multiple document element not allowed.");
+
+ // checking validity finished. then appending...
+
+
+ if (newChild == this || IsAncestor (newChild))
+ throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
+
+ }
+
public virtual void Normalize ()
{
- throw new NotImplementedException ();
+// if (tmpBuilder == null)
+ tmpBuilder = new StringBuilder ();
+// tmpBuilder.Length = 0;
+ int count = this.ChildNodes.Count;
+ int start = 0;
+ for (int i = 0; i < count; i++) {
+ XmlNode c = ChildNodes [i];
+ switch (c.NodeType) {
+ case XmlNodeType.Text:
+ case XmlNodeType.Whitespace:
+ case XmlNodeType.SignificantWhitespace:
+ tmpBuilder.Append (c.Value);
+ break;
+ default:
+ c.Normalize ();
+ NormalizeRange (start, i);
+ // Continue to normalize from next node.
+ start = i + 1;
+ break;
+ }
+ }
+ if (start < count) {
+ NormalizeRange (start, count);
+ }
+
+ tmpBuilder = null;
+ }
+
+ private void NormalizeRange (int start, int i)
+ {
+ int keepPos = -1;
+ // If Texts and Whitespaces are mixed, Text takes precedence to remain.
+ // i.e. Whitespace should be removed.
+ for (int j = start; j < i; j++) {
+ XmlNode keep = ChildNodes [j];
+ if (keep.NodeType == XmlNodeType.Text) {
+ keepPos = j;
+ break;
+ }
+ else if (keep.NodeType == XmlNodeType.SignificantWhitespace)
+ keepPos = j;
+ // but don't break up to find Text nodes.
+ }
+ // But if no Texts and one or more Whitespaces, then the first
+ if (keepPos < 0 && i > start)
+ keepPos = 0;
+
+ if (keepPos >= 0) {
+ for (int del = start; del < keepPos; del++)
+ RemoveChild (ChildNodes [del]);
+ for (int del = keepPos + 1; del < i; del++)
+ RemoveChild (ChildNodes [del]);
+ }
+
+ ChildNodes [keepPos].Value = tmpBuilder.ToString ();
+ tmpBuilder.Length = 0;
}
public virtual XmlNode PrependChild (XmlNode newChild)
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]
public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
{
XPathNavigator nav = CreateNavigator ();
return SelectSingleNode (xpath, null);
}
- [MonoTODO]
public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
{
XPathNavigator nav = CreateNavigator ();
return ((XmlDocumentNavigator) iter.Current).Node;
}
-// internal void SetParentNode (XmlNode parent)
-// {
-// parentNode = parent;
-// }
-
- [MonoTODO]
public virtual bool Supports (string feature, string version)
{
- throw new NotImplementedException ();
+ if (String.Compare (feature, "xml", true) == 0 // not case-sensitive
+ && (String.Compare (version, "1.0", true) == 0
+ || String.Compare (version, "2.0", true) == 0))
+ return true;
+ else
+ return false;
}
public abstract void WriteContentTo (XmlWriter w);
// 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;
while(el != null) {
foreach(XmlAttribute attr in el.Attributes) {
if(attr.Prefix == "xmlns") {
- if (nsmgr.LookupNamespace (attr.LocalName) == null)
+ if (nsmgr.LookupNamespace (attr.LocalName) != attr.Value)
nsmgr.AddNamespace (attr.LocalName, attr.Value);
} else if(attr.Name == "xmlns") {
- if(nsmgr.LookupNamespace (String.Empty) == null)
+ if(nsmgr.LookupNamespace (String.Empty) != attr.Value)
nsmgr.AddNamespace (String.Empty, attr.Value);
}
}