2 // Mono.Xml.XPath.XPathEditableDocument
5 // Atsushi Enomoto <atsushi@ximian.com>
9 // Yet another implementation of editable XPathNavigator.
10 // (Even runnable under MS.NET 2.0)
12 // By rewriting XPathEditableDocument.CreateNavigator() as just to
13 // create XmlDocumentNavigator, XmlDocumentEditableNavigator could be used
14 // as to implement XmlDocument.CreateNavigator().
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 using System.Collections;
41 using System.ComponentModel;
44 using System.Xml.Schema;
45 using System.Xml.XPath;
46 using System.Xml.Serialization;
48 namespace Mono.Xml.XPath
50 internal class XPathEditableDocument : IXPathNavigable
54 public XPathEditableDocument (XmlNode node)
63 public XPathNavigator CreateNavigator ()
65 return new XmlDocumentEditableNavigator (this);
69 internal delegate void XmlWriterClosedEventHandler (
72 internal class XmlDocumentInsertionWriter : XmlWriter
77 Stack nodeStack = new Stack ();
79 public XmlDocumentInsertionWriter (XmlNode owner, XmlNode nextSibling)
81 this.parent = (XmlNode) owner;
83 throw new InvalidOperationException ();
84 switch (parent.NodeType) {
85 case XmlNodeType.Document:
86 current = ((XmlDocument) parent).CreateDocumentFragment ();
88 case XmlNodeType.Element:
89 current = parent.OwnerDocument.CreateDocumentFragment ();
92 throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", parent.NodeType));
94 this.nextSibling = nextSibling;
95 state = WriteState.Content;
99 XmlAttribute attribute;
101 public override WriteState WriteState {
102 get { return state; }
105 public override void Close ()
107 while (nodeStack.Count > 0) {
108 XmlNode n = nodeStack.Pop () as XmlNode;
109 n.AppendChild (current);
112 parent.InsertBefore ((XmlDocumentFragment) current, nextSibling);
117 internal event XmlWriterClosedEventHandler Closed;
119 internal XmlNode AppendedFirstChild;
121 public override void Flush ()
125 public override string LookupPrefix (string ns)
127 return current.GetPrefixOfNamespace (ns);
130 public override void WriteStartAttribute (string prefix, string name, string ns)
132 if (state != WriteState.Content)
133 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
134 attribute = current.OwnerDocument.CreateAttribute (prefix, name, ns);
135 state = WriteState.Attribute;
138 public override void WriteProcessingInstruction (string name, string value)
140 XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
141 current.AppendChild (pi);
144 public override void WriteComment (string text)
146 XmlComment comment = current.OwnerDocument.CreateComment (text);
147 current.AppendChild (comment);
150 public override void WriteCData (string text)
152 XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
153 current.AppendChild (cdata);
156 public override void WriteStartElement (string prefix, string name, string ns)
158 XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
159 current.AppendChild (el);
160 nodeStack.Push (current);
164 public override void WriteEndElement ()
166 if (nodeStack.Count == 0)
167 throw new InvalidOperationException ("No element is opened.");
168 current = nodeStack.Pop () as XmlNode;
171 public override void WriteFullEndElement ()
173 XmlElement el = current as XmlElement;
179 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
181 throw new NotSupportedException ();
184 public override void WriteStartDocument ()
186 throw new NotSupportedException ();
189 public override void WriteStartDocument (bool standalone)
191 throw new NotSupportedException ();
194 public override void WriteEndDocument ()
196 throw new NotSupportedException ();
199 public override void WriteBase64 (byte [] data, int start, int length)
201 WriteString (Convert.ToBase64String (data, start, length));
204 public override void WriteRaw (char [] raw, int start, int length)
206 throw new NotSupportedException ();
209 public override void WriteRaw (string raw)
211 throw new NotSupportedException ();
214 public override void WriteSurrogateCharEntity (char msb, char lsb)
216 throw new NotSupportedException ();
219 public override void WriteCharEntity (char c)
221 throw new NotSupportedException ();
224 public override void WriteEntityRef (string entname)
226 if (state != WriteState.Attribute)
227 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
228 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
231 public override void WriteChars (char [] data, int start, int length)
233 WriteString (new string (data, start, length));
236 public override void WriteString (string text)
238 if (attribute != null)
239 attribute.Value += text;
241 XmlText t = current.OwnerDocument.CreateTextNode (text);
242 current.AppendChild (t);
246 public override void WriteWhitespace (string text)
248 if (state != WriteState.Attribute)
249 current.AppendChild (current.OwnerDocument.CreateTextNode (text));
250 else if (attribute.ChildNodes.Count == 0)
251 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
253 attribute.Value += text;
256 public override void WriteEndAttribute ()
258 XmlElement element = current as XmlElement;
259 if (state != WriteState.Attribute || element == null)
260 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
261 element.SetAttributeNode (attribute);
263 state = WriteState.Content;
267 internal class XmlDocumentAttributeWriter : XmlWriter
271 public XmlDocumentAttributeWriter (XmlNode owner)
273 element = owner as XmlElement;
275 throw new ArgumentException ("To write attributes, current node must be an element.");
276 state = WriteState.Content;
280 XmlAttribute attribute;
282 public override WriteState WriteState {
283 get { return state; }
286 public override void Close ()
290 public override void Flush ()
294 public override string LookupPrefix (string ns)
296 return element.GetPrefixOfNamespace (ns);
299 public override void WriteStartAttribute (string prefix, string name, string ns)
301 if (state != WriteState.Content)
302 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
303 attribute = element.OwnerDocument.CreateAttribute (prefix, name, ns);
304 state = WriteState.Attribute;
307 public override void WriteProcessingInstruction (string name, string value)
309 throw new NotSupportedException ();
312 public override void WriteComment (string text)
314 throw new NotSupportedException ();
317 public override void WriteCData (string text)
319 throw new NotSupportedException ();
322 public override void WriteStartElement (string prefix, string name, string ns)
324 throw new NotSupportedException ();
327 public override void WriteEndElement ()
329 throw new NotSupportedException ();
332 public override void WriteFullEndElement ()
334 throw new NotSupportedException ();
337 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
339 throw new NotSupportedException ();
342 public override void WriteStartDocument ()
344 throw new NotSupportedException ();
347 public override void WriteStartDocument (bool standalone)
349 throw new NotSupportedException ();
352 public override void WriteEndDocument ()
354 throw new NotSupportedException ();
357 public override void WriteBase64 (byte [] data, int start, int length)
359 throw new NotSupportedException ();
362 public override void WriteRaw (char [] raw, int start, int length)
364 throw new NotSupportedException ();
367 public override void WriteRaw (string raw)
369 throw new NotSupportedException ();
372 public override void WriteSurrogateCharEntity (char msb, char lsb)
374 throw new NotSupportedException ();
377 public override void WriteCharEntity (char c)
379 throw new NotSupportedException ();
382 public override void WriteEntityRef (string entname)
384 if (state != WriteState.Attribute)
385 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
386 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
389 public override void WriteChars (char [] data, int start, int length)
391 WriteString (new string (data, start, length));
394 public override void WriteString (string text)
396 if (state != WriteState.Attribute)
397 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
398 attribute.Value += text;
401 public override void WriteWhitespace (string text)
403 if (state != WriteState.Attribute)
404 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
405 if (attribute.ChildNodes.Count == 0)
406 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
408 attribute.Value += text;
411 public override void WriteEndAttribute ()
413 if (state != WriteState.Attribute)
414 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
415 element.SetAttributeNode (attribute);
417 state = WriteState.Content;
421 internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
423 static readonly bool isXmlDocumentNavigatorImpl;
425 static XmlDocumentEditableNavigator ()
427 isXmlDocumentNavigatorImpl =
428 (typeof (XmlDocumentEditableNavigator).Assembly
429 == typeof (XmlDocument).Assembly);
432 XPathEditableDocument document;
433 XPathNavigator navigator;
435 public XmlDocumentEditableNavigator (XPathEditableDocument doc)
438 if (isXmlDocumentNavigatorImpl)
439 navigator = new XmlDocumentNavigator (doc.Node);
441 navigator = doc.CreateNavigator ();
444 public XmlDocumentEditableNavigator (XmlDocumentEditableNavigator nav)
446 document = nav.document;
447 navigator = nav.navigator.Clone ();
450 public override string BaseURI {
451 get { return navigator.BaseURI; }
454 public override bool IsEmptyElement {
455 get { return navigator.IsEmptyElement; }
458 public override string LocalName {
459 get { return navigator.LocalName; }
462 public override XmlNameTable NameTable {
463 get { return navigator.NameTable; }
466 public override string Name {
467 get { return navigator.Name; }
470 public override string NamespaceURI {
471 get { return navigator.NamespaceURI; }
474 public override XPathNodeType NodeType {
475 get { return navigator.NodeType; }
478 public override string Prefix {
479 get { return navigator.Prefix; }
482 public override IXmlSchemaInfo SchemaInfo {
483 get { return navigator.SchemaInfo; }
486 public override object UnderlyingObject {
487 get { return navigator.UnderlyingObject; }
490 public override string Value {
491 get { return navigator.Value; }
494 public override XPathNavigator Clone ()
496 return new XmlDocumentEditableNavigator (this);
499 public override XPathNavigator CreateNavigator ()
501 return navigator.Clone ();
504 public XmlNode GetNode ()
506 return ((IHasXmlNode) navigator).GetNode ();
509 public override bool IsSamePosition (XPathNavigator other)
511 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
513 return navigator.IsSamePosition (nav.navigator);
515 return navigator.IsSamePosition (nav);
518 public override bool MoveTo (XPathNavigator other)
520 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
522 return navigator.MoveTo (nav.navigator);
524 return navigator.MoveTo (nav);
527 public override bool MoveToFirstAttribute ()
529 return navigator.MoveToFirstAttribute ();
532 public override bool MoveToFirstChild ()
534 return navigator.MoveToFirstChild ();
537 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
539 return navigator.MoveToFirstNamespace (scope);
542 public override bool MoveToId (string id)
544 return navigator.MoveToId (id);
547 public override bool MoveToNext ()
549 return navigator.MoveToNext ();
552 public override bool MoveToNextAttribute ()
554 return navigator.MoveToNextAttribute ();
557 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
559 return navigator.MoveToNextNamespace (scope);
562 public override bool MoveToParent ()
564 return navigator.MoveToParent ();
567 public override bool MoveToPrevious ()
569 return navigator.MoveToPrevious ();
572 public override XmlWriter AppendChild ()
574 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
576 throw new InvalidOperationException ("Should not happen.");
577 return new XmlDocumentInsertionWriter (n, null);
580 public override void DeleteRange (XPathNavigator lastSiblingToDelete)
582 if (lastSiblingToDelete == null)
583 throw new ArgumentNullException ();
585 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
587 if (lastSiblingToDelete is IHasXmlNode)
588 end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
589 // After removal, it moves to parent node.
590 if (!navigator.MoveToParent ())
591 throw new InvalidOperationException ("There is no parent to remove current node.");
593 if (end == null || start.ParentNode != end.ParentNode)
594 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
596 XmlNode parent = start.ParentNode;
599 for (XmlNode n = start; loop; n = next) {
601 next = n.NextSibling;
602 parent.RemoveChild (n);
606 public override XmlWriter ReplaceRange (XPathNavigator nav)
609 throw new ArgumentNullException ();
611 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
613 if (nav is IHasXmlNode)
614 end = ((IHasXmlNode) nav).GetNode ();
615 if (end == null || start.ParentNode != end.ParentNode)
616 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
618 XmlDocumentInsertionWriter w =
619 (XmlDocumentInsertionWriter) InsertBefore ();
621 // local variables to anonymous delegate
622 XPathNavigator prev = Clone ();
623 if (!prev.MoveToPrevious ())
625 XPathNavigator parentNav = Clone ();
626 parentNav.MoveToParent ();
628 w.Closed += delegate (XmlWriter w) {
629 XmlNode parent = start.ParentNode;
632 for (XmlNode n = start; loop; n = next) {
634 next = n.NextSibling;
635 parent.RemoveChild (n);
649 public override XmlWriter InsertBefore ()
651 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
652 return new XmlDocumentInsertionWriter (n.ParentNode, n);
655 public override XmlWriter CreateAttributes ()
657 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
658 return new XmlDocumentAttributeWriter (n);
661 public override void DeleteSelf ()
663 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
664 if (!navigator.MoveToNext ())
665 navigator.MoveToParent ();
666 if (n.ParentNode == null)
667 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
668 n.ParentNode.RemoveChild (n);
671 public override void ReplaceSelf (XmlReader reader)
673 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
674 XmlNode p = n.ParentNode;
676 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
678 bool movenext = false;
679 if (!MoveToPrevious ())
684 XmlDocument doc = p.NodeType == XmlNodeType.Document ?
685 p as XmlDocument : p.OwnerDocument;
687 if (reader.ReadState == ReadState.Initial) {
693 p.AppendChild (doc.ReadNode (reader));
698 p.AppendChild (doc.ReadNode (reader));
701 throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
711 public override void SetValue (string value)
713 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
714 while (n.FirstChild != null)
715 n.RemoveChild (n.FirstChild);