2005-12-13 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Tue, 13 Dec 2005 10:46:25 +0000 (10:46 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Tue, 13 Dec 2005 10:46:25 +0000 (10:46 -0000)
* 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."

* XPathNavigator.cs : InsertAfter() should raise an error before
  MoveToNext() when current node is either attribute or namespace.

* XPathEditableNavigatorTests.cs :
  Added more tests for InsertAfter() and InsertBefore().
  Added tests for DeleteRange() and ReplaceRange().

svn path=/trunk/mcs/; revision=54276

mcs/class/System.XML/Mono.Xml.XPath/ChangeLog
mcs/class/System.XML/Mono.Xml.XPath/XPathEditableDocument.cs
mcs/class/System.XML/System.Xml.XPath/ChangeLog
mcs/class/System.XML/System.Xml.XPath/XPathNavigator.cs
mcs/class/System.XML/Test/System.Xml.XPath/ChangeLog
mcs/class/System.XML/Test/System.Xml.XPath/XPathEditableNavigatorTests.cs

index 8069c200f822bd7b2db8ea797b8471cba047e751..09280a3c2c43f8c1597a0d920a80a952948eb098 100644 (file)
@@ -1,3 +1,12 @@
+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 :
index 54436e261fa780a4a8d8e25e6588cbdd15e3d53a..0146a0724edfed038d6cc727464faa90c369a814 100644 (file)
@@ -66,23 +66,30 @@ namespace Mono.Xml.XPath
                }
        }
 
+       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;
@@ -97,8 +104,20 @@ namespace Mono.Xml.XPath
 
                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 ()
                {
                }
@@ -119,30 +138,25 @@ namespace Mono.Xml.XPath
                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;
                }
@@ -555,6 +569,75 @@ namespace Mono.Xml.XPath
                        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 ();
index 4c55237564e422a4b3d23e7b6eb295ae14d77ddb..f8cc3cc241cb8fb9935528e4594a6b695dfdaa57 100644 (file)
@@ -1,3 +1,8 @@
+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
index 05e75eabb2a37d996a0313cc03ea5a453f18938e..9f8a04a7ff298f3e505195a0c38423ce135a7195 100644 (file)
@@ -1009,6 +1009,7 @@ namespace System.Xml.XPath
                                new XmlParserContext (NameTable, nsmgr, null, XmlSpace.None));
                }
 
+               // must override it.
                public virtual XmlWriter AppendChild ()
                {
                        throw new NotSupportedException ();
@@ -1052,23 +1053,24 @@ namespace System.Xml.XPath
                        }
                }
 
+               // 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 ();
@@ -1076,13 +1078,19 @@ namespace System.Xml.XPath
        
                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)
@@ -1090,7 +1098,6 @@ namespace System.Xml.XPath
                        InsertAfter (CreateFragmentReader (xmlFragments));
                }
 
-               [MonoTODO]
                public virtual void InsertAfter (XmlReader reader)
                {
                        using (XmlWriter w = InsertAfter ()) {
index e89cbe82a1a5342c195f8737c2bc5365748e9c39..8943911a019eabaee07bb4d589a4fb4f970c4aa2 100644 (file)
@@ -1,3 +1,9 @@
+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
index d8c935db6df1e9f8c8d2a332aa1e0ff13e3ce003..8fd955dae9e32c264198871d8c636919344f82c8 100644 (file)
@@ -213,6 +213,37 @@ namespace MonoTests.System.Xml.XPath
                        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 ()
                {
@@ -264,6 +295,161 @@ namespace MonoTests.System.Xml.XPath
                        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);
+               }
        }
 }