Jo Shields 3c1f479b9d Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
2015-04-07 09:35:12 +01:00

806 lines
21 KiB
C#

//
// Mono.Xml.XPath.XPathEditableDocument
//
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
// (C)2004 Novell Inc.
//
// Yet another implementation of editable XPathNavigator.
// (Even runnable under MS.NET 2.0)
//
// By rewriting XPathEditableDocument.CreateNavigator() as just to
// create XmlDocumentNavigator, XmlDocumentEditableNavigator could be used
// as to implement XmlDocument.CreateNavigator().
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.ComponentModel;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Xml.Serialization;
namespace Mono.Xml.XPath
{
internal class XPathEditableDocument : IXPathNavigable
{
XmlNode node;
public XPathEditableDocument (XmlNode node)
{
this.node = node;
}
public XmlNode Node {
get { return node; }
}
public XPathNavigator CreateNavigator ()
{
return new XmlDocumentEditableNavigator (this);
}
}
internal delegate void XmlWriterClosedEventHandler (
XmlWriter writer);
internal class XmlDocumentInsertionWriter : XmlWriter
{
XmlNode parent;
XmlNode current;
XmlNode nextSibling;
WriteState state;
XmlAttribute attribute;
public XmlDocumentInsertionWriter (XmlNode owner, XmlNode nextSibling)
{
this.parent = (XmlNode) owner;
if (parent == null)
throw new InvalidOperationException ();
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;
}
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 ()
{
}
public override string LookupPrefix (string ns)
{
return current.GetPrefixOfNamespace (ns);
}
public override void WriteStartAttribute (string prefix, string name, string ns)
{
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;
}
public override void WriteProcessingInstruction (string name, string value)
{
XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
current.AppendChild (pi);
}
public override void WriteComment (string text)
{
XmlComment comment = current.OwnerDocument.CreateComment (text);
current.AppendChild (comment);
}
public override void WriteCData (string text)
{
XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
current.AppendChild (cdata);
}
public override void WriteStartElement (string prefix, string name, string ns)
{
if (prefix == null && ns != null && ns.Length > 0)
prefix = LookupPrefix (ns);
XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
current.AppendChild (el);
current = el;
}
public override void WriteEndElement ()
{
current = current.ParentNode;
if (current == null)
throw new InvalidOperationException ("No element is opened.");
}
public override void WriteFullEndElement ()
{
XmlElement el = current as XmlElement;
if (el != null)
el.IsEmpty = false;
WriteEndElement ();
}
public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
{
throw new NotSupportedException ();
}
public override void WriteStartDocument ()
{
throw new NotSupportedException ();
}
public override void WriteStartDocument (bool standalone)
{
throw new NotSupportedException ();
}
public override void WriteEndDocument ()
{
throw new NotSupportedException ();
}
public override void WriteBase64 (byte [] data, int start, int length)
{
WriteString (Convert.ToBase64String (data, start, length));
}
public override void WriteRaw (char [] raw, int start, int length)
{
throw new NotSupportedException ();
}
public override void WriteRaw (string raw)
{
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)
{
throw new NotSupportedException ();
}
public override void WriteCharEntity (char c)
{
throw new NotSupportedException ();
}
public override void WriteEntityRef (string entname)
{
if (state != WriteState.Attribute)
throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
}
public override void WriteChars (char [] data, int start, int length)
{
WriteString (new string (data, start, length));
}
public override void WriteString (string text)
{
if (attribute != null)
attribute.Value += text;
else {
XmlText t = current.OwnerDocument.CreateTextNode (text);
current.AppendChild (t);
}
}
public override void WriteWhitespace (string text)
{
if (state != WriteState.Attribute)
current.AppendChild (current.OwnerDocument.CreateTextNode (text));
else if (attribute.ChildNodes.Count == 0)
attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
else
attribute.Value += text;
}
public override void WriteEndAttribute ()
{
// 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.");
element.SetAttributeNode (attribute);
attribute = null;
state = WriteState.Content;
}
}
internal class XmlDocumentAttributeWriter : XmlWriter
{
XmlElement element;
WriteState state;
XmlAttribute attribute;
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;
}
public override WriteState WriteState {
get { return state; }
}
public override void Close ()
{
}
public override void Flush ()
{
}
public override string LookupPrefix (string ns)
{
return element.GetPrefixOfNamespace (ns);
}
public override void WriteStartAttribute (string prefix, string name, string ns)
{
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;
}
public override void WriteProcessingInstruction (string name, string value)
{
throw new NotSupportedException ();
}
public override void WriteComment (string text)
{
throw new NotSupportedException ();
}
public override void WriteCData (string text)
{
throw new NotSupportedException ();
}
public override void WriteStartElement (string prefix, string name, string ns)
{
throw new NotSupportedException ();
}
public override void WriteEndElement ()
{
throw new NotSupportedException ();
}
public override void WriteFullEndElement ()
{
throw new NotSupportedException ();
}
public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
{
throw new NotSupportedException ();
}
public override void WriteStartDocument ()
{
throw new NotSupportedException ();
}
public override void WriteStartDocument (bool standalone)
{
throw new NotSupportedException ();
}
public override void WriteEndDocument ()
{
throw new NotSupportedException ();
}
public override void WriteBase64 (byte [] data, int start, int length)
{
throw new NotSupportedException ();
}
public override void WriteRaw (char [] raw, int start, int length)
{
throw new NotSupportedException ();
}
public override void WriteRaw (string raw)
{
throw new NotSupportedException ();
}
public override void WriteSurrogateCharEntity (char msb, char lsb)
{
throw new NotSupportedException ();
}
public override void WriteCharEntity (char c)
{
throw new NotSupportedException ();
}
public override void WriteEntityRef (string entname)
{
if (state != WriteState.Attribute)
throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
}
public override void WriteChars (char [] data, int start, int length)
{
WriteString (new string (data, start, length));
}
public override void WriteString (string text)
{
if (state != WriteState.Attribute)
throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
attribute.Value += text;
}
public override void WriteWhitespace (string text)
{
if (state != WriteState.Attribute)
throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
if (attribute.ChildNodes.Count == 0)
attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
else
attribute.Value += text;
}
public override void WriteEndAttribute ()
{
if (state != WriteState.Attribute)
throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
element.SetAttributeNode (attribute);
attribute = null;
state = WriteState.Content;
}
}
internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
{
static readonly bool isXmlDocumentNavigatorImpl;
static XmlDocumentEditableNavigator ()
{
isXmlDocumentNavigatorImpl =
(typeof (XmlDocumentEditableNavigator).Assembly
== typeof (XmlDocument).Assembly);
}
XPathEditableDocument document;
XPathNavigator navigator;
public XmlDocumentEditableNavigator (XPathEditableDocument doc)
{
document = doc;
if (isXmlDocumentNavigatorImpl)
navigator = new XmlDocumentNavigator (doc.Node);
else
navigator = doc.CreateNavigator ();
}
public XmlDocumentEditableNavigator (XmlDocumentEditableNavigator nav)
{
document = nav.document;
navigator = nav.navigator.Clone ();
}
public override string BaseURI {
get { return navigator.BaseURI; }
}
public override bool CanEdit {
get { return true; }
}
public override bool IsEmptyElement {
get { return navigator.IsEmptyElement; }
}
public override string LocalName {
get { return navigator.LocalName; }
}
public override XmlNameTable NameTable {
get { return navigator.NameTable; }
}
public override string Name {
get { return navigator.Name; }
}
public override string NamespaceURI {
get { return navigator.NamespaceURI; }
}
public override XPathNodeType NodeType {
get { return navigator.NodeType; }
}
public override string Prefix {
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);
}
public override XPathNavigator CreateNavigator ()
{
return Clone ();
}
public XmlNode GetNode ()
{
return ((IHasXmlNode) navigator).GetNode ();
}
public override bool IsSamePosition (XPathNavigator other)
{
XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
if (nav != null)
return navigator.IsSamePosition (nav.navigator);
else
return navigator.IsSamePosition (nav);
}
public override bool MoveTo (XPathNavigator other)
{
XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
if (nav != null)
return navigator.MoveTo (nav.navigator);
else
return navigator.MoveTo (nav);
}
public override bool MoveToFirstAttribute ()
{
return navigator.MoveToFirstAttribute ();
}
public override bool MoveToFirstChild ()
{
return navigator.MoveToFirstChild ();
}
public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
{
return navigator.MoveToFirstNamespace (scope);
}
public override bool MoveToId (string id)
{
return navigator.MoveToId (id);
}
public override bool MoveToNext ()
{
return navigator.MoveToNext ();
}
public override bool MoveToNextAttribute ()
{
return navigator.MoveToNextAttribute ();
}
public override bool MoveToNextNamespace (XPathNamespaceScope scope)
{
return navigator.MoveToNextNamespace (scope);
}
public override bool MoveToParent ()
{
return navigator.MoveToParent ();
}
public override bool MoveToPrevious ()
{
return navigator.MoveToPrevious ();
}
public override XmlWriter AppendChild ()
{
XmlNode n = ((IHasXmlNode) navigator).GetNode ();
if (n == null)
throw new InvalidOperationException ("Should not happen.");
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 new XmlDocumentInsertionWriter (n.ParentNode, n);
}
public override XmlWriter CreateAttributes ()
{
XmlNode n = ((IHasXmlNode) navigator).GetNode ();
return new XmlDocumentAttributeWriter (n);
}
public override void DeleteSelf ()
{
XmlNode n = ((IHasXmlNode) navigator).GetNode ();
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 ();
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 ();
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);
}
}
}