//
// Author:
// Kral Ferch <kral_ferch@hotmail.com>
+// Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
//
// (C) 2002 Kral Ferch
+// (C) 2002 Atsushi Enomoto
//
using System;
using System.Collections;
using System.IO;
+using System.Text;
using System.Xml.XPath;
namespace System.Xml
#region Constructors
- protected internal XmlNode (XmlDocument ownerDocument)
+ internal XmlNode (XmlDocument ownerDocument)
{
this.ownerDocument = ownerDocument;
}
#region Properties
- public virtual XmlAttributeCollection Attributes
- {
+ public virtual XmlAttributeCollection Attributes {
get { return null; }
}
- [MonoTODO]
- public virtual string BaseURI
- {
- get { throw new NotImplementedException (); }
+ public virtual string BaseURI {
+ get {
+ // Isn't it conformant to W3C XML Base Recommendation?
+ // As far as I tested, there are not...
+ return (ParentNode != null) ? ParentNode.BaseURI : OwnerDocument.BaseURI;
+ }
}
public virtual XmlNodeList ChildNodes {
get {
- return new XmlNodeListChildren(this);
+ return new XmlNodeListChildren (this);
}
}
get { return LastChild != null; }
}
- [MonoTODO]
+ [MonoTODO("confirm whether this way is right for each not-overriden types.")]
public virtual string InnerText {
- get { throw new NotImplementedException (); }
+ get {
+ StringBuilder builder = new StringBuilder ();
+ AppendChildValues (this, builder);
+ return builder.ToString ();
+ }
+
set { throw new NotImplementedException (); }
}
+ private void AppendChildValues (XmlNode parent, StringBuilder builder)
+ {
+ XmlNode node = parent.FirstChild;
+
+ while (node != null) {
+ if (node.NodeType == XmlNodeType.Text)
+ builder.Append (node.Value);
+ AppendChildValues (node, builder);
+ node = node.NextSibling;
+ }
+ }
+
[MonoTODO("Setter.")]
public virtual string InnerXml {
get {
StringWriter sw = new StringWriter ();
XmlTextWriter xtw = new XmlTextWriter (sw);
- WriteContentTo(xtw);
+ WriteContentTo (xtw);
- return sw.GetStringBuilder().ToString();
+ return sw.GetStringBuilder ().ToString ();
}
set { throw new NotImplementedException (); }
public abstract string Name { get; }
- [MonoTODO]
public virtual string NamespaceURI {
- get { throw new NotImplementedException (); }
+ get { return String.Empty; }
}
public virtual XmlNode NextSibling {
public abstract XmlNodeType NodeType { get; }
- [MonoTODO]
+ internal virtual XPathNodeType XPathNodeType {
+ get {
+ return (XPathNodeType) (-1);
+ }
+ }
+
public virtual string OuterXml {
get {
StringWriter sw = new StringWriter ();
XmlTextWriter xtw = new XmlTextWriter (sw);
- WriteTo(xtw);
+ WriteTo (xtw);
- return sw.GetStringBuilder().ToString();
+ return sw.GetStringBuilder ().ToString ();
}
}
get { return parentNode; }
}
- [MonoTODO]
public virtual string Prefix {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
+ get { return String.Empty; }
+ set {}
}
public virtual XmlNode PreviousSibling {
get { return null; }
}
- [MonoTODO]
public virtual string Value {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
+ get { return null; }
+ set { throw new InvalidOperationException ("This node does not have a value"); }
+ }
+
+ internal virtual string XmlLang {
+ get {
+ if(Attributes != null)
+ foreach(XmlAttribute attr in Attributes)
+ if(attr.Name == "xml:lang")
+ return attr.Value;
+ return (ParentNode != null) ? ParentNode.XmlLang : OwnerDocument.XmlLang;
+ }
+ }
+
+ internal virtual XmlSpace XmlSpace {
+ get {
+ if(Attributes != null) {
+ foreach(XmlAttribute attr in Attributes) {
+ if(attr.Name == "xml:space") {
+ switch(attr.Value) {
+ case "preserve": return XmlSpace.Preserve;
+ case "default": return XmlSpace.Default;
+ }
+ break;
+ }
+ }
+ }
+ return (ParentNode != null) ? ParentNode.XmlSpace : OwnerDocument.XmlSpace;
+ }
}
#endregion
public virtual XmlNode AppendChild (XmlNode newChild)
{
- if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute) {
- XmlLinkedNode newLinkedChild = (XmlLinkedNode)newChild;
- XmlLinkedNode lastLinkedChild = LastLinkedChild;
-
- newLinkedChild.parentNode = this;
-
- if (lastLinkedChild != null) {
- newLinkedChild.NextLinkedSibling = lastLinkedChild.NextLinkedSibling;
- lastLinkedChild.NextLinkedSibling = newLinkedChild;
- } else
- newLinkedChild.NextLinkedSibling = newLinkedChild;
-
- LastLinkedChild = newLinkedChild;
-
- return newChild;
- } else
- throw new InvalidOperationException();
+ // I assume that AppendChild(n) equals to InsertAfter(n, this.LastChild) or InsertBefore(n, null)
+ return InsertBefore (newChild, null);
}
- [MonoTODO]
public virtual XmlNode Clone ()
{
- throw new NotImplementedException ();
+ // By MS document, it is equivalent to CloneNode(true).
+ return this.CloneNode (true);
}
public abstract XmlNode CloneNode (bool deep);
[MonoTODO]
public XPathNavigator CreateNavigator ()
{
- throw new NotImplementedException ();
+ return new XmlDocumentNavigator (this);
}
public IEnumerator GetEnumerator ()
{
- return new XmlNodeListChildren(this).GetEnumerator();
+ return new XmlNodeListChildren (this).GetEnumerator ();
}
- [MonoTODO]
+ [MonoTODO("performance problem.")]
public virtual string GetNamespaceOfPrefix (string prefix)
{
- throw new NotImplementedException ();
+ XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
+ return nsmgr.LookupNamespace (prefix);
}
- [MonoTODO]
+ [MonoTODO("performance problem.")]
public virtual string GetPrefixOfNamespace (string namespaceURI)
{
- throw new NotImplementedException ();
+ XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
+ string ns = nsmgr.LookupPrefix (namespaceURI);
+ return (ns != null) ? ns : String.Empty;
}
object ICloneable.Clone ()
return GetEnumerator ();
}
- [MonoTODO]
public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
{
- throw new NotImplementedException ();
+ // I assume that insertAfter(n1, n2) equals to InsertBefore(n1, n2.PreviousSibling).
+
+ // I took this way because rather than calling InsertAfter() from InsertBefore()
+ // because current implementation of 'NextSibling' looks faster than 'PreviousSibling'.
+ XmlNode argNode = null;
+ if(refChild != null)
+ argNode = refChild.NextSibling;
+ else if(ChildNodes.Count > 0)
+ argNode = FirstChild;
+ return InsertBefore (newChild, argNode);
}
- [MonoTODO]
+ [MonoTODO("If inserted node is entity reference, then check conforming entity. Wait for DTD implementation.")]
public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
{
- throw new NotImplementedException ();
+ 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...
+
+ 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;
+ }
+ else
+ throw new InvalidOperationException ();
}
[MonoTODO]
throw new NotImplementedException ();
}
- [MonoTODO]
public virtual XmlNode PrependChild (XmlNode newChild)
{
- throw new NotImplementedException ();
+ return InsertAfter (newChild, null);
}
public virtual void RemoveAll ()
{
- LastLinkedChild = null;
+ XmlNode next = null;
+ for (XmlNode node = FirstChild; node != null; node = next) {
+ next = node.NextSibling;
+ RemoveChild (node);
+ }
}
public virtual XmlNode RemoveChild (XmlNode oldChild)
{
- if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute)
- {
+ 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);
+
+ if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || NodeType == XmlNodeType.DocumentFragment) {
if (IsReadOnly)
- throw new ArgumentException();
+ throw new ArgumentException ();
- if (Object.ReferenceEquals(LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals(LastLinkedChild, oldChild))
+ if (Object.ReferenceEquals (LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals (LastLinkedChild, oldChild))
LastLinkedChild = null;
else {
XmlLinkedNode oldLinkedChild = (XmlLinkedNode)oldChild;
XmlLinkedNode beforeLinkedChild = LastLinkedChild;
- while (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, LastLinkedChild) && !Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
+ while (!Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, LastLinkedChild) && !Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
beforeLinkedChild = beforeLinkedChild.NextLinkedSibling;
- if (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
- throw new ArgumentException();
+ if (!Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
+ throw new ArgumentException ();
beforeLinkedChild.NextLinkedSibling = oldLinkedChild.NextLinkedSibling;
oldLinkedChild.NextLinkedSibling = null;
}
+ ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
+ oldChild.parentNode = null; // clear parent 'after' above logic.
+
return oldChild;
}
else
- throw new ArgumentException();
+ throw new ArgumentException ();
}
- [MonoTODO]
public virtual XmlNode ReplaceChild (XmlNode newChild, XmlNode oldChild)
{
- throw new NotImplementedException ();
+ 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;
+ }
+ foreach(XmlNode n in ChildNodes) {
+ if(n == oldChild) {
+ XmlNode prev = oldChild.PreviousSibling;
+ RemoveChild (oldChild);
+ InsertAfter (newChild, prev);
+ break;
+ }
+ }
+ return oldChild;
}
- [MonoTODO]
- public virtual XmlNodeList SelectNodes (string xpath)
+ public XmlNodeList SelectNodes (string xpath)
{
- throw new NotImplementedException ();
+ return SelectNodes (xpath, null);
}
[MonoTODO]
- public virtual XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
+ public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
{
- throw new NotImplementedException ();
+ XPathNavigator nav = CreateNavigator ();
+ XPathExpression expr = nav.Compile (xpath);
+ if (nsmgr != null)
+ expr.SetContext (nsmgr);
+ XPathNodeIterator iter = nav.Select (expr);
+ ArrayList rgNodes = new ArrayList ();
+ while (iter.MoveNext ())
+ {
+ rgNodes.Add (((XmlDocumentNavigator) iter.Current).Node);
+ }
+ return new XmlNodeArrayList (rgNodes);
}
- [MonoTODO]
- public virtual XmlNode SelectSingleNode (string xpath)
+ public XmlNode SelectSingleNode (string xpath)
{
- throw new NotImplementedException ();
+ return SelectSingleNode (xpath, null);
}
[MonoTODO]
- public virtual XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
+ public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
{
- throw new NotImplementedException ();
+ XPathNavigator nav = CreateNavigator ();
+ XPathExpression expr = nav.Compile (xpath);
+ if (nsmgr != null)
+ expr.SetContext (nsmgr);
+ XPathNodeIterator iter = nav.Select (expr);
+ if (!iter.MoveNext ())
+ return null;
+ return ((XmlDocumentNavigator) iter.Current).Node;
}
internal void SetParentNode (XmlNode parent)
public abstract void WriteTo (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;
+ XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
+ XmlElement el = null;
+ switch(this.NodeType) {
+ case XmlNodeType.Attribute:
+ el = ((XmlAttribute)this).OwnerElement;
+ break;
+ case XmlNodeType.Element:
+ el = this as XmlElement;
+ break;
+ default:
+ el = this.ParentNode as XmlElement;
+ break;
+ }
+
+ while(el != null) {
+ foreach(XmlAttribute attr in el.Attributes) {
+ if(attr.Prefix == "xmlns" || (attr.Name == "xmlns" && attr.Prefix == String.Empty)) {
+ if(nsmgr.LookupNamespace (attr.LocalName) == null )
+ nsmgr.AddNamespace (attr.LocalName, attr.Value);
+ }
+ }
+ // When reached to document, then it will set null value :)
+ el = el.ParentNode as XmlElement;
+ }
+ return nsmgr;
+ }
#endregion
}
}