{
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; }
}
{
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 ()
{
}
{
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;
}
{
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)
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)
else {
XmlText t = current.OwnerDocument.CreateTextNode (text);
current.AppendChild (t);
- document.AppendChild (current, t);
}
}
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;
}
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; }
}
{
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;
}
{
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;
get { return navigator.BaseURI; }
}
+ public override bool CanEdit {
+ get { return true; }
+ }
+
public override bool IsEmptyElement {
get { return navigator.IsEmptyElement; }
}
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 navigator.Clone ();
+ return Clone ();
}
public XmlNode GetNode ()
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);
}
}
}