+2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XPathEditableDocument.cs :
+ - Now it does not append "written" nodes until Close() is invoked.
+ - Use XmlDocumentFragment to store incomplete tree fragment.
+ - Implemented DeleteRange() and ReplaceRange().
+ - Added "Closed" event for ReplaceRange() to "not remove until
+ Close() is called."
+
2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
* XPathEditableDocument.cs :
}
}
+ internal delegate void XmlWriterClosedEventHandler (
+ XmlWriter writer);
+
internal class XmlDocumentInsertionWriter : XmlWriter
{
+ XmlNode parent;
XmlNode current;
XmlNode nextSibling;
Stack nodeStack = new Stack ();
public XmlDocumentInsertionWriter (XmlNode owner, XmlNode nextSibling)
{
- this.current = (XmlNode) owner;
- if (current == null)
+ this.parent = (XmlNode) owner;
+ if (parent == null)
throw new InvalidOperationException ();
- switch (current.NodeType) {
+ switch (parent.NodeType) {
case XmlNodeType.Document:
+ current = ((XmlDocument) parent).CreateDocumentFragment ();
+ break;
case XmlNodeType.Element:
+ current = parent.OwnerDocument.CreateDocumentFragment ();
break;
default:
- throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", current.NodeType));
+ throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", parent.NodeType));
}
this.nextSibling = nextSibling;
state = WriteState.Content;
public override void Close ()
{
+ while (nodeStack.Count > 0) {
+ XmlNode n = nodeStack.Pop () as XmlNode;
+ n.AppendChild (current);
+ current = n;
+ }
+ parent.InsertBefore ((XmlDocumentFragment) current, nextSibling);
+ if (Closed != null)
+ Closed (this);
}
+ internal event XmlWriterClosedEventHandler Closed;
+
+ internal XmlNode AppendedFirstChild;
+
public override void Flush ()
{
}
public override void WriteProcessingInstruction (string name, string value)
{
XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
- current.InsertBefore (pi, nextSibling);
+ current.AppendChild (pi);
}
public override void WriteComment (string text)
{
XmlComment comment = current.OwnerDocument.CreateComment (text);
- current.InsertBefore (comment, nextSibling);
+ current.AppendChild (comment);
}
public override void WriteCData (string text)
{
XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
- current.InsertBefore (cdata, nextSibling);
+ current.AppendChild (cdata);
}
public override void WriteStartElement (string prefix, string name, string ns)
{
- XmlDocument doc = current.OwnerDocument;
- if (doc == null)
- doc = current as XmlDocument;
- if (doc == null)
- throw new SystemException ("Should not happen.");
- XmlElement el = doc.CreateElement (prefix, name, ns);
- current.InsertBefore (el, nextSibling);
+ XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
+ current.AppendChild (el);
nodeStack.Push (current);
current = el;
}
return new XmlDocumentInsertionWriter (n, null);
}
+ public override void DeleteRange (XPathNavigator lastSiblingToDelete)
+ {
+ if (lastSiblingToDelete == null)
+ throw new ArgumentNullException ();
+
+ XmlNode start = ((IHasXmlNode) navigator).GetNode ();
+ XmlNode end = null;
+ if (lastSiblingToDelete is IHasXmlNode)
+ end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
+ // After removal, it moves to parent node.
+ if (!navigator.MoveToParent ())
+ throw new InvalidOperationException ("There is no parent to remove current node.");
+
+ if (end == null || start.ParentNode != end.ParentNode)
+ throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
+
+ XmlNode parent = start.ParentNode;
+ XmlNode next;
+ bool loop = true;
+ for (XmlNode n = start; loop; n = next) {
+ loop = n != end;
+ next = n.NextSibling;
+ parent.RemoveChild (n);
+ }
+ }
+
+ public override XmlWriter ReplaceRange (XPathNavigator nav)
+ {
+ if (nav == null)
+ throw new ArgumentNullException ();
+
+ XmlNode start = ((IHasXmlNode) navigator).GetNode ();
+ XmlNode end = null;
+ if (nav is IHasXmlNode)
+ end = ((IHasXmlNode) nav).GetNode ();
+ if (end == null || start.ParentNode != end.ParentNode)
+ throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
+
+ XmlDocumentInsertionWriter w =
+ (XmlDocumentInsertionWriter) InsertBefore ();
+
+ // local variables to anonymous delegate
+ XPathNavigator prev = Clone ();
+ if (!prev.MoveToPrevious ())
+ prev = null;
+ XPathNavigator parentNav = Clone ();
+ parentNav.MoveToParent ();
+
+ w.Closed += delegate (XmlWriter w) {
+ XmlNode parent = start.ParentNode;
+ XmlNode next;
+ bool loop = true;
+ for (XmlNode n = start; loop; n = next) {
+ loop = n != end;
+ next = n.NextSibling;
+ parent.RemoveChild (n);
+ }
+ if (prev != null) {
+ MoveTo (prev);
+ MoveToNext ();
+ } else {
+ MoveTo (parentNav);
+ MoveToFirstChild ();
+ }
+ };
+
+ return w;
+ }
+
public override XmlWriter InsertBefore ()
{
XmlNode n = ((IHasXmlNode) navigator).GetNode ();
+2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XPathNavigator.cs : InsertAfter() should raise an error before
+ MoveToNext() when current node is either attribute or namespace.
+
2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
* XPathNavigator.cs : InsertAfter() should append children after it
new XmlParserContext (NameTable, nsmgr, null, XmlSpace.None));
}
+ // must override it.
public virtual XmlWriter AppendChild ()
{
throw new NotSupportedException ();
}
}
+ // must override it.
public virtual XmlWriter CreateAttributes ()
{
throw new NotSupportedException ();
}
+ // must override it.
public virtual void DeleteSelf ()
{
throw new NotSupportedException ();
}
- [MonoTODO ("no concrete implementation yet")]
+ // must override it.
public virtual void DeleteRange (XPathNavigator nav)
{
throw new NotSupportedException ();
}
- [MonoTODO ("no concrete implementation yet")]
public virtual XmlWriter ReplaceRange (XPathNavigator nav)
{
throw new NotSupportedException ();
public virtual XmlWriter InsertAfter ()
{
+ switch (NodeType) {
+ case XPathNodeType.Root:
+ case XPathNodeType.Attribute:
+ case XPathNodeType.Namespace:
+ throw new InvalidOperationException (String.Format ("Insertion after {0} is not allowed.", NodeType));
+ }
XPathNavigator nav = Clone ();
if (nav.MoveToNext ())
return nav.InsertBefore ();
else if (nav.MoveToParent ())
return nav.AppendChild ();
else
- throw new InvalidOperationException ("Insertion after Root node is not allowed.");
+ throw new InvalidOperationException ("Could not move to parent to insert sibling node");
}
public virtual void InsertAfter (string xmlFragments)
InsertAfter (CreateFragmentReader (xmlFragments));
}
- [MonoTODO]
public virtual void InsertAfter (XmlReader reader)
{
using (XmlWriter w = InsertAfter ()) {
+2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XPathEditableNavigatorTests.cs :
+ Added more tests for InsertAfter() and InsertBefore().
+ Added tests for DeleteRange() and ReplaceRange().
+
2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
* XPathEditableNavigatorTests.cs : added tests for InsertAfter and
nav.InsertAfter ();
}
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void InsertAfterAttribute ()
+ {
+ XPathNavigator nav = GetInstance ("<root a='b'/>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstAttribute ();
+ nav.InsertAfter ();
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void InsertAfterNamespace ()
+ {
+ XPathNavigator nav = GetInstance ("<root xmlns='urn:foo'/>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstNamespace ();
+ nav.InsertAfter ();
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ // xmlns:xml='...', which is likely to have XmlElement or XmlDocument as its node.
+ public void InsertAfterNamespace2 ()
+ {
+ XPathNavigator nav = GetInstance ("<root />");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstNamespace ();
+ nav.InsertAfter ();
+ }
+
[Test]
public void InsertBefore ()
{
XPathNavigator nav = GetInstance ("<root/>");
nav.InsertBefore ();
}
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void InsertBeforeAttribute ()
+ {
+ XPathNavigator nav = GetInstance ("<root a='b'/>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstAttribute ();
+ nav.InsertBefore ();
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void InsertBeforeNamespace ()
+ {
+ XPathNavigator nav = GetInstance ("<root xmlns='urn:foo'/>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstNamespace ();
+ nav.InsertBefore ();
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ // xmlns:xml='...', which is likely to have XmlElement or XmlDocument as its node.
+ public void InsertBeforeNamespace2 ()
+ {
+ XPathNavigator nav = GetInstance ("<root />");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstNamespace ();
+ nav.InsertBefore ();
+ }
+
+ [Test]
+ public void DeleteRange ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+ XPathNavigator end = nav.Clone ();
+ end.MoveToNext (); // <next>
+ end.MoveToNext (); // final
+ nav.DeleteRange (end);
+
+ AssertNavigator ("#1", nav,
+ XPathNodeType.Element,
+ String.Empty, // Prefix
+ "root", // LocalName
+ String.Empty, // NamespaceURI
+ "root", // Name
+ String.Empty, // Value
+ false, // HasAttributes
+ false, // HasChildren
+ false); // IsEmptyElement
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void DeleteRangeNullArg ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+ nav.DeleteRange (null);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void DeleteRangeInvalidArg ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+
+ XPathNavigator end = nav.Clone ();
+ end.MoveToNext (); // <next>
+ end.MoveToFirstChild (); // child
+ nav.DeleteRange (end);
+ }
+
+ [Test]
+ public void ReplaceRange ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+
+ XPathNavigator end = nav.Clone ();
+ end.MoveToNext (); // <next>
+ XmlWriter w = nav.ReplaceRange (end);
+
+ AssertNavigator ("#1", nav,
+ XPathNodeType.Element,
+ String.Empty, // Prefix
+ "foo", // LocalName
+ String.Empty, // NamespaceURI
+ "foo", // Name
+ String.Empty, // Value
+ false, // HasAttributes
+ true, // HasChildren
+ false); // IsEmptyElement
+
+ Assert.IsTrue (nav.MoveToParent (), "#1-2");
+
+ w.WriteStartElement ("whoa");
+ w.WriteEndElement ();
+ w.Close ();
+
+ AssertNavigator ("#2", nav,
+ XPathNodeType.Element,
+ String.Empty, // Prefix
+ "whoa", // LocalName
+ String.Empty, // NamespaceURI
+ "whoa", // Name
+ String.Empty, // Value
+ false, // HasAttributes
+ false, // HasChildren
+ true); // IsEmptyElement
+
+ Assert.IsTrue (nav.MoveToNext (), "#2-1");
+
+ AssertNavigator ("#3", nav,
+ XPathNodeType.Text,
+ String.Empty, // Prefix
+ String.Empty, // LocalName
+ String.Empty, // NamespaceURI
+ String.Empty, // Name
+ "final", // Value
+ false, // HasAttributes
+ false, // HasChildren
+ false); // IsEmptyElement
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void ReplaceRangeNullArg ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+ nav.ReplaceRange (null);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void ReplaceRangeInvalidArg ()
+ {
+ XPathNavigator nav = GetInstance ("<root><foo><bar/><baz/></foo><next>child<tmp/></next>final</root>");
+ nav.MoveToFirstChild ();
+ nav.MoveToFirstChild (); // <foo>
+
+ XPathNavigator end = nav.Clone ();
+ end.MoveToNext (); // <next>
+ end.MoveToFirstChild (); // child
+ nav.ReplaceRange (end);
+ }
}
}