[sgen] Restore hazard pointers in suspend signal handler. Fixes #15695.
[mono.git] / mcs / class / System.XML / Mono.Xml.XPath / XPathEditableDocument.cs
index 48d632e5655ae3b3a1be859e6adb0d3fa1c8a35c..1c21cae56243233a0ac587c61787e25b13519841 100644 (file)
@@ -49,69 +49,13 @@ namespace Mono.Xml.XPath
 {
        internal class XPathEditableDocument : IXPathNavigable
        {
-               /*
-               public static void Main ()
-               {
-                       try {
-#if true
-                               XmlDocument doc = new XmlDocument ();
-                               XPathEditableDocument pd = new XPathEditableDocument (doc);
-                               XPathNavigator nav = pd.CreateNavigator ();
-                               IChangeTracking xp = pd;
-#else
-                               XPathDocument doc = new XPathDocument ();
-                               XPathNavigator nav = doc.CreateNavigator ();
-                               IChangeTracking xp = doc;
-#endif
-                               doc.LoadXml ("<root/>");
-                               nav.MoveToFirstChild (); // root
-                               XmlWriter w = nav.AppendChild ();
-                               Console.WriteLine (((IChangeTracking) xp).IsChanged);
-                               w.WriteElementString ("foo", "foo_text");
-                               w.WriteElementString ("bar", "bar_text");
-                               w.WriteStartElement ("hoge");
-                               w.WriteAttributeString ("fuga", "fugafuga");
-                               w.WriteAttributeString ("unya", "unyaunya");
-                               w.WriteFullEndElement ();
-                               w.Close ();
-
-                               w = nav.CreateAttributes ();
-                               w.WriteStartAttribute ("namara");
-                               w.WriteString ("mokera");
-                               w.WriteEndAttribute ();
-                               w.WriteAttributeString ("beccho", "giccho");
-                               w.Close ();
-
-                               nav.MoveToRoot ();
-                               nav.MoveToFirstChild ();
-                               nav.MoveToFirstChild ();
-                               nav.DeleteSelf (); // delete foo
-                               Console.WriteLine (nav.Name);
-                               nav.MoveToNext ();
-                               Console.WriteLine (nav.Name);
-                               Console.WriteLine (nav.MoveToFirstAttribute ());
-                               nav.DeleteSelf (); // delete fuga
-
-                               doc.Save (Console.Out);
-                       } catch (Exception ex) {
-                               Console.WriteLine (ex);
-                       }
-               }
-               */
-
                XmlNode node;
 
-               ArrayList changes = new ArrayList ();
-
                public XPathEditableDocument (XmlNode node)
                {
                        this.node = node;
                }
 
-               public virtual bool CanEdit {
-                       get { return true; }
-               }
-
                public XmlNode Node {
                        get { return node; }
                }
@@ -120,150 +64,55 @@ namespace Mono.Xml.XPath
                {
                        return new XmlDocumentEditableNavigator (this);
                }
-
-               public XmlWriter CreateWriter ()
-               {
-                       return CreateNavigator ().AppendChild ();
-               }
-
-               public bool HasChanges ()
-               {
-                       return IsChanged;
-               }
-
-               #region IRevertibleChangeTracking/IChangeTracking
-               public bool IsChanged {
-                       get { return changes.Count != 0; }
-               }
-
-               public void AcceptChanges ()
-               {
-                       changes.Clear ();
-               }
-
-               public void RejectChanges ()
-               {
-                       for (int i = changes.Count - 1; i >= 0; i--) {
-                               Insertion ins = changes [i] as Insertion;
-                               if (ins != null) {
-                                       ins.ParentNode.RemoveChild (ins.InsertedNode);
-                                       continue;
-                               }
-                               
-                               Removal rem = changes [i] as Removal;
-                               if (rem != null) {
-                                       if (rem.RemovedNode.NodeType == XmlNodeType.Attribute) {
-                                               XmlElement el = (XmlElement) rem.OwnerNode;
-                                               el.SetAttributeNode ((XmlAttribute) rem.RemovedNode);
-                                       }
-                                       else
-                                               rem.OwnerNode.InsertBefore (rem.RemovedNode, rem.NextSibling);
-                                       continue;
-                               }
-                               AttributeUpdate au = changes [i] as AttributeUpdate;
-                               if (au != null) {
-                                       if (au.OldAttribute != null)
-                                               au.Element.SetAttributeNode (au.OldAttribute);
-                                       else
-                                               au.Element.RemoveAttributeNode (au.NewAttribute);
-                                       continue;
-                               }
-                       }
-                       changes.Clear ();
-               }
-               #endregion
-
-               #region IXmlSerializable
-               public void WriteXml (XmlWriter writer)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               public void ReadXml (XmlReader reader)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               public XmlSchema GetSchema ()
-               {
-                       throw new NotImplementedException ();
-               }
-               #endregion
-
-               internal bool DeleteNode (XmlNode node)
-               {
-                       Removal rem = new Removal ();
-                       if (node.NodeType == XmlNodeType.Attribute) {
-                               XmlAttribute attr = node as XmlAttribute;
-                               rem.OwnerNode = attr.OwnerElement;
-                               rem.RemovedNode = node;
-                               attr.OwnerElement.RemoveAttributeNode (attr);
-                               return false;
-                       } else {
-                               rem.OwnerNode = node.ParentNode;
-                               rem.NextSibling = node.NextSibling;
-                               rem.RemovedNode = node;
-                               node.ParentNode.RemoveChild (node);
-                               return rem.NextSibling != null;
-                       }
-               }
-
-               internal XmlWriter CreateInsertionWriter (XmlNode owner, XmlNode previousSibling)
-               {
-                       return new XmlDocumentInsertionWriter (owner, previousSibling, this);
-               }
-
-               internal XmlWriter CreateAttributesWriter (XmlNode owner)
-               {
-                       return new XmlDocumentAttributeWriter (owner, this);
-               }
-
-               internal void AttributeUpdate (XmlElement element, XmlAttribute oldAttr, XmlAttribute newAttr)
-               {
-                       AttributeUpdate au = new AttributeUpdate ();
-                       au.Element = element;
-                       au.OldAttribute = oldAttr;
-                       au.NewAttribute = newAttr;
-                       changes.Add (au);
-               }
-               
-               internal void AppendChild (XmlNode parent, XmlNode child)
-               {
-                       Insertion ins = new Insertion ();
-                       ins.ParentNode = parent;
-                       ins.InsertedNode = child;
-                       changes.Add (ins);
-               }
        }
 
+       internal delegate void XmlWriterClosedEventHandler (
+               XmlWriter writer);
+
        internal class XmlDocumentInsertionWriter : XmlWriter
        {
+               XmlNode parent;
                XmlNode current;
-               XmlNode previousSibling;
-               XPathEditableDocument document;
-               Stack nodeStack = new Stack ();
+               XmlNode nextSibling;
+               WriteState state;
+               XmlAttribute attribute;
 
-               public XmlDocumentInsertionWriter (XmlNode owner, XmlNode previousSibling, XPathEditableDocument doc)
+               public XmlDocumentInsertionWriter (XmlNode owner, XmlNode nextSibling)
                {
-                       this.current = (XmlNode) owner;
-                       if (current == null)
+                       this.parent = (XmlNode) owner;
+                       if (parent == null)
                                throw new InvalidOperationException ();
-                       this.previousSibling = previousSibling;
-                       this.document = doc;
+                       switch (parent.NodeType) {
+                       case XmlNodeType.Document:
+                               current = ((XmlDocument) parent).CreateDocumentFragment ();
+                               break;
+                       case XmlNodeType.DocumentFragment:
+                       case XmlNodeType.Element:
+                               current = parent.OwnerDocument.CreateDocumentFragment ();
+                               break;
+                       default:
+                               throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", parent.NodeType));
+                       }
+                       this.nextSibling = nextSibling;
                        state = WriteState.Content;
                }
 
-               WriteState state;
-               XmlAttribute attribute;
-
                public override WriteState WriteState {
                        get { return state; }
                }
 
                public override void Close ()
                {
+                       while (current.ParentNode != null)
+                               current = current.ParentNode;
+
+                       parent.InsertBefore ((XmlDocumentFragment) current, nextSibling);
+                       if (Closed != null)
+                               Closed (this);
                }
 
+               internal event XmlWriterClosedEventHandler Closed;
+
                public override void Flush ()
                {
                }
@@ -277,6 +126,8 @@ namespace Mono.Xml.XPath
                {
                        if (state != WriteState.Content)
                                throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
+                       if (prefix == null && ns != null && ns.Length > 0)
+                               prefix = LookupPrefix (ns);
                        attribute = current.OwnerDocument.CreateAttribute (prefix, name, ns);
                        state = WriteState.Attribute;
                }
@@ -285,50 +136,42 @@ namespace Mono.Xml.XPath
                {
                        XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
                        current.AppendChild (pi);
-                       document.AppendChild (current, pi);
                }
 
                public override void WriteComment (string text)
                {
                        XmlComment comment = current.OwnerDocument.CreateComment (text);
                        current.AppendChild (comment);
-                       document.AppendChild (current, comment);
                }
 
                public override void WriteCData (string text)
                {
                        XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
                        current.AppendChild (cdata);
-                       document.AppendChild (current, 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);
+                       if (prefix == null && ns != null && ns.Length > 0)
+                               prefix = LookupPrefix (ns);
+                       XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
                        current.AppendChild (el);
-                       document.AppendChild (current, el);
-                       nodeStack.Push (current);
                        current = el;
                }
 
                public override void WriteEndElement ()
                {
-                       if (nodeStack.Count == 0)
+                       current = current.ParentNode;
+                       if (current == null)
                                throw new InvalidOperationException ("No element is opened.");
-                       current = nodeStack.Pop () as XmlNode;
                }
 
                public override void WriteFullEndElement ()
                {
-                       WriteEndElement ();
                        XmlElement el = current as XmlElement;
                        if (el != null)
                                el.IsEmpty = false;
+                       WriteEndElement ();
                }
 
                public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
@@ -363,7 +206,20 @@ namespace Mono.Xml.XPath
 
                public override void WriteRaw (string raw)
                {
-                       throw new NotSupportedException ();
+                       XmlReader reader = new XmlTextReader(new System.IO.StringReader(raw));
+                       WriteRaw(reader);
+               }
+
+               private void WriteRaw(XmlReader reader)
+               {
+                       if (reader != null && reader.NodeType == XmlNodeType.Element)
+                       {
+                               WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
+                               WriteAttributes (reader, true);
+                               WriteRaw (reader.ReadSubtree ());
+                               WriteEndElement ();
+
+                       }
                }
 
                public override void WriteSurrogateCharEntity (char msb, char lsb)
@@ -395,7 +251,6 @@ namespace Mono.Xml.XPath
                        else {
                                XmlText t = current.OwnerDocument.CreateTextNode (text);
                                current.AppendChild (t);
-                               document.AppendChild (current, t);
                        }
                }
 
@@ -411,10 +266,13 @@ namespace Mono.Xml.XPath
 
                public override void WriteEndAttribute ()
                {
-                       XmlElement element = current as XmlElement;
+                       // when the writer is for AppendChild() and the root 
+                       // node is element, it allows to write attributes
+                       // (IMHO incorrectly: isn't it append "child" ???)
+                       XmlElement element = (current as XmlElement) ?? (nextSibling == null ? parent as XmlElement : null);
                        if (state != WriteState.Attribute || element == null)
                                throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
-                       document.AttributeUpdate (element, element.SetAttributeNode (attribute), attribute);
+                       element.SetAttributeNode (attribute);
                        attribute = null;
                        state = WriteState.Content;
                }
@@ -423,20 +281,17 @@ namespace Mono.Xml.XPath
        internal class XmlDocumentAttributeWriter : XmlWriter
        {
                XmlElement element;
-               XPathEditableDocument document;
+               WriteState state;
+               XmlAttribute attribute;
 
-               public XmlDocumentAttributeWriter (XmlNode owner, XPathEditableDocument doc)
+               public XmlDocumentAttributeWriter (XmlNode owner)
                {
                        element = owner as XmlElement;
                        if (element == null)
                                throw new ArgumentException ("To write attributes, current node must be an element.");
                        state = WriteState.Content;
-                       document = doc;
                }
 
-               WriteState state;
-               XmlAttribute attribute;
-
                public override WriteState WriteState {
                        get { return state; }
                }
@@ -458,6 +313,8 @@ namespace Mono.Xml.XPath
                {
                        if (state != WriteState.Content)
                                throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
+                       if (prefix == null && ns != null && ns.Length > 0)
+                               prefix = LookupPrefix (ns);
                        attribute = element.OwnerDocument.CreateAttribute (prefix, name, ns);
                        state = WriteState.Attribute;
                }
@@ -570,37 +427,12 @@ namespace Mono.Xml.XPath
                {
                        if (state != WriteState.Attribute)
                                throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
-                       document.AttributeUpdate (element, element.SetAttributeNode (attribute), attribute);
+                       element.SetAttributeNode (attribute);
                        attribute = null;
                        state = WriteState.Content;
                }
        }
 
-       internal class Insertion
-       {
-               // AppendChild : last child / true
-               // InsertBefore : current node / false
-               // InsertAfter : current node / true
-               // PrependChild : first child / false
-               public XmlNode ParentNode;
-               public XmlNode InsertedNode;
-               public bool Afterward;
-       }
-
-       internal class Removal
-       {
-               public XmlNode OwnerNode;
-               public XmlNode NextSibling;
-               public XmlNode RemovedNode;
-       }
-
-       internal class AttributeUpdate
-       {
-               public XmlElement Element;
-               public XmlAttribute NewAttribute;
-               public XmlAttribute OldAttribute;
-       }
-
        internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
        {
                static readonly bool isXmlDocumentNavigatorImpl;
@@ -634,6 +466,10 @@ namespace Mono.Xml.XPath
                        get { return navigator.BaseURI; }
                }
 
+               public override bool CanEdit {
+                       get { return true; }
+               }
+
                public override bool IsEmptyElement {
                        get { return navigator.IsEmptyElement; }
                }
@@ -662,10 +498,30 @@ namespace Mono.Xml.XPath
                        get { return navigator.Prefix; }
                }
 
+               public override IXmlSchemaInfo SchemaInfo {
+                       get { return navigator.SchemaInfo; }
+               }
+
+               public override object UnderlyingObject {
+                       get { return navigator.UnderlyingObject; }
+               }
+
                public override string Value {
                        get { return navigator.Value; }
                }
 
+               public override string XmlLang {
+                       get { return navigator.XmlLang; }
+               }
+
+               public override bool HasChildren {
+                       get { return navigator.HasChildren; }
+               }
+
+               public override bool HasAttributes {
+                       get { return navigator.HasAttributes; }
+               }
+
                public override XPathNavigator Clone ()
                {
                        return new XmlDocumentEditableNavigator (this);
@@ -673,7 +529,7 @@ namespace Mono.Xml.XPath
 
                public override XPathNavigator CreateNavigator ()
                {
-                       return navigator.Clone ();
+                       return Clone ();
                }
 
                public XmlNode GetNode ()
@@ -749,37 +605,201 @@ namespace Mono.Xml.XPath
                        XmlNode n = ((IHasXmlNode) navigator).GetNode ();
                        if (n == null)
                                throw new InvalidOperationException ("Should not happen.");
-                       return document.CreateInsertionWriter (n, null);
+                       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 writer) {
+                               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 ();
-                       return document.CreateInsertionWriter (n.ParentNode, n.PreviousSibling);
+                       return new XmlDocumentInsertionWriter (n.ParentNode, n);
                }
 
                public override XmlWriter CreateAttributes ()
                {
                        XmlNode n = ((IHasXmlNode) navigator).GetNode ();
-                       return document.CreateInsertionWriter (n, null);
+                       return new XmlDocumentAttributeWriter (n);
                }
 
                public override void DeleteSelf ()
                {
                        XmlNode n = ((IHasXmlNode) navigator).GetNode ();
-                       if (!navigator.MoveToNext ())
+                       XmlAttribute a = n as XmlAttribute;
+                       if (a != null) {
+                               if (a.OwnerElement == null)
+                                       throw new InvalidOperationException ("This attribute node cannot be removed since it has no owner element.");
+                               navigator.MoveToParent ();
+                               a.OwnerElement.RemoveAttributeNode (a);
+                       } else {
+                               if (n.ParentNode == null)
+                                       throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
                                navigator.MoveToParent ();
-                       document.DeleteNode (n);
+                               n.ParentNode.RemoveChild (n);
+                       }
+               }
+
+               public override void ReplaceSelf (XmlReader reader)
+               {
+                       XmlNode n = ((IHasXmlNode) navigator).GetNode ();
+                       XmlNode p = n.ParentNode;
+                       if (p == null)
+                               throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
+
+                       bool movenext = false;
+                       if (!MoveToPrevious ())
+                               MoveToParent ();
+                       else
+                               movenext = true;
+
+                       XmlDocument doc = p.NodeType == XmlNodeType.Document ?
+                               p as XmlDocument : p.OwnerDocument;
+                       bool error = false;
+                       if (reader.ReadState == ReadState.Initial) {
+                               reader.Read ();
+                               if (reader.EOF)
+                                       error = true;
+                               else
+                                       while (!reader.EOF)
+                                               p.AppendChild (doc.ReadNode (reader));
+                       } else {
+                               if (reader.EOF)
+                                       error = true;
+                               else
+                                       p.AppendChild (doc.ReadNode (reader));
+                       }
+                       if (error)
+                               throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
+
+                       p.RemoveChild (n);
+
+                       if (movenext)
+                               MoveToNext ();
+                       else
+                               MoveToFirstChild ();
                }
 
                public override void SetValue (string value)
                {
                        XmlNode n = ((IHasXmlNode) navigator).GetNode ();
-                       foreach (XmlNode c in n.ChildNodes)
-                               document.DeleteNode (c);
-                       XmlWriter w = document.CreateInsertionWriter (n, null);
-                       w.WriteValue (value);
-                       w.Close ();
+                       while (n.FirstChild != null)
+                               n.RemoveChild (n.FirstChild);
+                       n.InnerText = value;
+               }
+
+               public override void MoveToRoot ()
+               {
+                       navigator.MoveToRoot ();
+               }
+
+               public override bool MoveToNamespace (string name)
+               {
+                       return navigator.MoveToNamespace (name);
+               }
+
+               public override bool MoveToFirst ()
+               {
+                       return navigator.MoveToFirst ();
+               }
+
+               public override bool MoveToAttribute (string localName, string namespaceURI)
+               {
+                       return navigator.MoveToAttribute (localName, namespaceURI);
+               }
+
+               public override bool IsDescendant (XPathNavigator nav)
+               {
+                       XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
+                       if (e != null)
+                               return navigator.IsDescendant (e.navigator);
+                       else
+                               return navigator.IsDescendant (nav);
+               }
+
+               public override string GetNamespace (string name)
+               {
+                       return navigator.GetNamespace (name);
+               }
+
+               public override string GetAttribute (string localName, string namespaceURI)
+               {
+                       return navigator.GetAttribute (localName, namespaceURI);
+               }
+
+               public override XmlNodeOrder ComparePosition (XPathNavigator nav)
+               {
+                       XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
+                       if (e != null)
+                               return navigator.ComparePosition (e.navigator);
+                       else
+                               return navigator.ComparePosition (nav);
                }
        }
 }