From aba5cde6e552af5255f8c54b80c79a19f6f655c5 Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Tue, 13 Dec 2005 10:46:25 +0000 Subject: [PATCH] 2005-12-13 Atsushi Enomoto * 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 | 9 + .../Mono.Xml.XPath/XPathEditableDocument.cs | 111 +++++++++-- .../System.XML/System.Xml.XPath/ChangeLog | 5 + .../System.Xml.XPath/XPathNavigator.cs | 15 +- .../Test/System.Xml.XPath/ChangeLog | 6 + .../XPathEditableNavigatorTests.cs | 186 ++++++++++++++++++ 6 files changed, 314 insertions(+), 18 deletions(-) diff --git a/mcs/class/System.XML/Mono.Xml.XPath/ChangeLog b/mcs/class/System.XML/Mono.Xml.XPath/ChangeLog index 8069c200f82..09280a3c2c4 100644 --- a/mcs/class/System.XML/Mono.Xml.XPath/ChangeLog +++ b/mcs/class/System.XML/Mono.Xml.XPath/ChangeLog @@ -1,3 +1,12 @@ +2005-12-13 Atsushi Enomoto + + * 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 * XPathEditableDocument.cs : diff --git a/mcs/class/System.XML/Mono.Xml.XPath/XPathEditableDocument.cs b/mcs/class/System.XML/Mono.Xml.XPath/XPathEditableDocument.cs index 54436e261fa..0146a0724ed 100644 --- a/mcs/class/System.XML/Mono.Xml.XPath/XPathEditableDocument.cs +++ b/mcs/class/System.XML/Mono.Xml.XPath/XPathEditableDocument.cs @@ -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 (); diff --git a/mcs/class/System.XML/System.Xml.XPath/ChangeLog b/mcs/class/System.XML/System.Xml.XPath/ChangeLog index 4c55237564e..f8cc3cc241c 100644 --- a/mcs/class/System.XML/System.Xml.XPath/ChangeLog +++ b/mcs/class/System.XML/System.Xml.XPath/ChangeLog @@ -1,3 +1,8 @@ +2005-12-13 Atsushi Enomoto + + * XPathNavigator.cs : InsertAfter() should raise an error before + MoveToNext() when current node is either attribute or namespace. + 2005-12-13 Atsushi Enomoto * XPathNavigator.cs : InsertAfter() should append children after it diff --git a/mcs/class/System.XML/System.Xml.XPath/XPathNavigator.cs b/mcs/class/System.XML/System.Xml.XPath/XPathNavigator.cs index 05e75eabb2a..9f8a04a7ff2 100644 --- a/mcs/class/System.XML/System.Xml.XPath/XPathNavigator.cs +++ b/mcs/class/System.XML/System.Xml.XPath/XPathNavigator.cs @@ -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 ()) { diff --git a/mcs/class/System.XML/Test/System.Xml.XPath/ChangeLog b/mcs/class/System.XML/Test/System.Xml.XPath/ChangeLog index e89cbe82a1a..8943911a019 100644 --- a/mcs/class/System.XML/Test/System.Xml.XPath/ChangeLog +++ b/mcs/class/System.XML/Test/System.Xml.XPath/ChangeLog @@ -1,3 +1,9 @@ +2005-12-13 Atsushi Enomoto + + * XPathEditableNavigatorTests.cs : + Added more tests for InsertAfter() and InsertBefore(). + Added tests for DeleteRange() and ReplaceRange(). + 2005-12-13 Atsushi Enomoto * XPathEditableNavigatorTests.cs : added tests for InsertAfter and diff --git a/mcs/class/System.XML/Test/System.Xml.XPath/XPathEditableNavigatorTests.cs b/mcs/class/System.XML/Test/System.Xml.XPath/XPathEditableNavigatorTests.cs index d8c935db6df..8fd955dae9e 100644 --- a/mcs/class/System.XML/Test/System.Xml.XPath/XPathEditableNavigatorTests.cs +++ b/mcs/class/System.XML/Test/System.Xml.XPath/XPathEditableNavigatorTests.cs @@ -213,6 +213,37 @@ namespace MonoTests.System.Xml.XPath nav.InsertAfter (); } + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void InsertAfterAttribute () + { + XPathNavigator nav = GetInstance (""); + nav.MoveToFirstChild (); + nav.MoveToFirstAttribute (); + nav.InsertAfter (); + } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void InsertAfterNamespace () + { + XPathNavigator nav = GetInstance (""); + 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 (""); + nav.MoveToFirstChild (); + nav.MoveToFirstNamespace (); + nav.InsertAfter (); + } + [Test] public void InsertBefore () { @@ -264,6 +295,161 @@ namespace MonoTests.System.Xml.XPath XPathNavigator nav = GetInstance (""); nav.InsertBefore (); } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void InsertBeforeAttribute () + { + XPathNavigator nav = GetInstance (""); + nav.MoveToFirstChild (); + nav.MoveToFirstAttribute (); + nav.InsertBefore (); + } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void InsertBeforeNamespace () + { + XPathNavigator nav = GetInstance (""); + 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 (""); + nav.MoveToFirstChild (); + nav.MoveToFirstNamespace (); + nav.InsertBefore (); + } + + [Test] + public void DeleteRange () + { + XPathNavigator nav = GetInstance ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + XPathNavigator end = nav.Clone (); + end.MoveToNext (); // + 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 ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + nav.DeleteRange (null); + } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void DeleteRangeInvalidArg () + { + XPathNavigator nav = GetInstance ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + + XPathNavigator end = nav.Clone (); + end.MoveToNext (); // + end.MoveToFirstChild (); // child + nav.DeleteRange (end); + } + + [Test] + public void ReplaceRange () + { + XPathNavigator nav = GetInstance ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + + XPathNavigator end = nav.Clone (); + end.MoveToNext (); // + 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 ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + nav.ReplaceRange (null); + } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void ReplaceRangeInvalidArg () + { + XPathNavigator nav = GetInstance ("childfinal"); + nav.MoveToFirstChild (); + nav.MoveToFirstChild (); // + + XPathNavigator end = nav.Clone (); + end.MoveToNext (); // + end.MoveToFirstChild (); // child + nav.ReplaceRange (end); + } } } -- 2.25.1