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
78 XmlAttribute attribute;
80 public XmlDocumentInsertionWriter (XmlNode owner, XmlNode nextSibling)
82 this.parent = (XmlNode) owner;
84 throw new InvalidOperationException ();
85 switch (parent.NodeType) {
86 case XmlNodeType.Document:
87 current = ((XmlDocument) parent).CreateDocumentFragment ();
89 case XmlNodeType.DocumentFragment:
90 case XmlNodeType.Element:
91 current = parent.OwnerDocument.CreateDocumentFragment ();
94 throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", parent.NodeType));
96 this.nextSibling = nextSibling;
97 state = WriteState.Content;
100 public override WriteState WriteState {
101 get { return state; }
104 public override void Close ()
106 while (current.ParentNode != null)
107 current = current.ParentNode;
109 parent.InsertBefore ((XmlDocumentFragment) current, nextSibling);
114 internal event XmlWriterClosedEventHandler Closed;
116 public override void Flush ()
120 public override string LookupPrefix (string ns)
122 return current.GetPrefixOfNamespace (ns);
125 public override void WriteStartAttribute (string prefix, string name, string ns)
127 if (state != WriteState.Content)
128 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
129 if (prefix == null && ns != null && ns.Length > 0)
130 prefix = LookupPrefix (ns);
131 attribute = current.OwnerDocument.CreateAttribute (prefix, name, ns);
132 state = WriteState.Attribute;
135 public override void WriteProcessingInstruction (string name, string value)
137 XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
138 current.AppendChild (pi);
141 public override void WriteComment (string text)
143 XmlComment comment = current.OwnerDocument.CreateComment (text);
144 current.AppendChild (comment);
147 public override void WriteCData (string text)
149 XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
150 current.AppendChild (cdata);
153 public override void WriteStartElement (string prefix, string name, string ns)
155 if (prefix == null && ns != null && ns.Length > 0)
156 prefix = LookupPrefix (ns);
157 XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
158 current.AppendChild (el);
162 public override void WriteEndElement ()
164 current = current.ParentNode;
166 throw new InvalidOperationException ("No element is opened.");
169 public override void WriteFullEndElement ()
171 XmlElement el = current as XmlElement;
177 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
179 throw new NotSupportedException ();
182 public override void WriteStartDocument ()
184 throw new NotSupportedException ();
187 public override void WriteStartDocument (bool standalone)
189 throw new NotSupportedException ();
192 public override void WriteEndDocument ()
194 throw new NotSupportedException ();
197 public override void WriteBase64 (byte [] data, int start, int length)
199 WriteString (Convert.ToBase64String (data, start, length));
202 public override void WriteRaw (char [] raw, int start, int length)
204 throw new NotSupportedException ();
207 public override void WriteRaw (string raw)
209 throw new NotSupportedException ();
212 public override void WriteSurrogateCharEntity (char msb, char lsb)
214 throw new NotSupportedException ();
217 public override void WriteCharEntity (char c)
219 throw new NotSupportedException ();
222 public override void WriteEntityRef (string entname)
224 if (state != WriteState.Attribute)
225 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
226 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
229 public override void WriteChars (char [] data, int start, int length)
231 WriteString (new string (data, start, length));
234 public override void WriteString (string text)
236 if (attribute != null)
237 attribute.Value += text;
239 XmlText t = current.OwnerDocument.CreateTextNode (text);
240 current.AppendChild (t);
244 public override void WriteWhitespace (string text)
246 if (state != WriteState.Attribute)
247 current.AppendChild (current.OwnerDocument.CreateTextNode (text));
248 else if (attribute.ChildNodes.Count == 0)
249 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
251 attribute.Value += text;
254 public override void WriteEndAttribute ()
256 // when the writer is for AppendChild() and the root
257 // node is element, it allows to write attributes
258 // (IMHO incorrectly: isn't it append "child" ???)
259 XmlElement element = (current as XmlElement) ?? (nextSibling == null ? parent as XmlElement : null);
260 if (state != WriteState.Attribute || element == null)
261 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
262 element.SetAttributeNode (attribute);
264 state = WriteState.Content;
268 internal class XmlDocumentAttributeWriter : XmlWriter
272 XmlAttribute attribute;
274 public XmlDocumentAttributeWriter (XmlNode owner)
276 element = owner as XmlElement;
278 throw new ArgumentException ("To write attributes, current node must be an element.");
279 state = WriteState.Content;
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 if (prefix == null && ns != null && ns.Length > 0)
304 prefix = LookupPrefix (ns);
305 attribute = element.OwnerDocument.CreateAttribute (prefix, name, ns);
306 state = WriteState.Attribute;
309 public override void WriteProcessingInstruction (string name, string value)
311 throw new NotSupportedException ();
314 public override void WriteComment (string text)
316 throw new NotSupportedException ();
319 public override void WriteCData (string text)
321 throw new NotSupportedException ();
324 public override void WriteStartElement (string prefix, string name, string ns)
326 throw new NotSupportedException ();
329 public override void WriteEndElement ()
331 throw new NotSupportedException ();
334 public override void WriteFullEndElement ()
336 throw new NotSupportedException ();
339 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
341 throw new NotSupportedException ();
344 public override void WriteStartDocument ()
346 throw new NotSupportedException ();
349 public override void WriteStartDocument (bool standalone)
351 throw new NotSupportedException ();
354 public override void WriteEndDocument ()
356 throw new NotSupportedException ();
359 public override void WriteBase64 (byte [] data, int start, int length)
361 throw new NotSupportedException ();
364 public override void WriteRaw (char [] raw, int start, int length)
366 throw new NotSupportedException ();
369 public override void WriteRaw (string raw)
371 throw new NotSupportedException ();
374 public override void WriteSurrogateCharEntity (char msb, char lsb)
376 throw new NotSupportedException ();
379 public override void WriteCharEntity (char c)
381 throw new NotSupportedException ();
384 public override void WriteEntityRef (string entname)
386 if (state != WriteState.Attribute)
387 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
388 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
391 public override void WriteChars (char [] data, int start, int length)
393 WriteString (new string (data, start, length));
396 public override void WriteString (string text)
398 if (state != WriteState.Attribute)
399 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
400 attribute.Value += text;
403 public override void WriteWhitespace (string text)
405 if (state != WriteState.Attribute)
406 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
407 if (attribute.ChildNodes.Count == 0)
408 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
410 attribute.Value += text;
413 public override void WriteEndAttribute ()
415 if (state != WriteState.Attribute)
416 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
417 element.SetAttributeNode (attribute);
419 state = WriteState.Content;
423 internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
425 static readonly bool isXmlDocumentNavigatorImpl;
427 static XmlDocumentEditableNavigator ()
429 isXmlDocumentNavigatorImpl =
430 (typeof (XmlDocumentEditableNavigator).Assembly
431 == typeof (XmlDocument).Assembly);
434 XPathEditableDocument document;
435 XPathNavigator navigator;
437 public XmlDocumentEditableNavigator (XPathEditableDocument doc)
440 if (isXmlDocumentNavigatorImpl)
441 navigator = new XmlDocumentNavigator (doc.Node);
443 navigator = doc.CreateNavigator ();
446 public XmlDocumentEditableNavigator (XmlDocumentEditableNavigator nav)
448 document = nav.document;
449 navigator = nav.navigator.Clone ();
452 public override string BaseURI {
453 get { return navigator.BaseURI; }
456 public override bool CanEdit {
460 public override bool IsEmptyElement {
461 get { return navigator.IsEmptyElement; }
464 public override string LocalName {
465 get { return navigator.LocalName; }
468 public override XmlNameTable NameTable {
469 get { return navigator.NameTable; }
472 public override string Name {
473 get { return navigator.Name; }
476 public override string NamespaceURI {
477 get { return navigator.NamespaceURI; }
480 public override XPathNodeType NodeType {
481 get { return navigator.NodeType; }
484 public override string Prefix {
485 get { return navigator.Prefix; }
488 public override IXmlSchemaInfo SchemaInfo {
489 get { return navigator.SchemaInfo; }
492 public override object UnderlyingObject {
493 get { return navigator.UnderlyingObject; }
496 public override string Value {
497 get { return navigator.Value; }
500 public override string XmlLang {
501 get { return navigator.XmlLang; }
504 public override bool HasChildren {
505 get { return navigator.HasChildren; }
508 public override bool HasAttributes {
509 get { return navigator.HasAttributes; }
512 public override XPathNavigator Clone ()
514 return new XmlDocumentEditableNavigator (this);
517 public override XPathNavigator CreateNavigator ()
519 return navigator.Clone ();
522 public XmlNode GetNode ()
524 return ((IHasXmlNode) navigator).GetNode ();
527 public override bool IsSamePosition (XPathNavigator other)
529 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
531 return navigator.IsSamePosition (nav.navigator);
533 return navigator.IsSamePosition (nav);
536 public override bool MoveTo (XPathNavigator other)
538 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
540 return navigator.MoveTo (nav.navigator);
542 return navigator.MoveTo (nav);
545 public override bool MoveToFirstAttribute ()
547 return navigator.MoveToFirstAttribute ();
550 public override bool MoveToFirstChild ()
552 return navigator.MoveToFirstChild ();
555 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
557 return navigator.MoveToFirstNamespace (scope);
560 public override bool MoveToId (string id)
562 return navigator.MoveToId (id);
565 public override bool MoveToNext ()
567 return navigator.MoveToNext ();
570 public override bool MoveToNextAttribute ()
572 return navigator.MoveToNextAttribute ();
575 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
577 return navigator.MoveToNextNamespace (scope);
580 public override bool MoveToParent ()
582 return navigator.MoveToParent ();
585 public override bool MoveToPrevious ()
587 return navigator.MoveToPrevious ();
590 public override XmlWriter AppendChild ()
592 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
594 throw new InvalidOperationException ("Should not happen.");
595 return new XmlDocumentInsertionWriter (n, null);
598 public override void DeleteRange (XPathNavigator lastSiblingToDelete)
600 if (lastSiblingToDelete == null)
601 throw new ArgumentNullException ();
603 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
605 if (lastSiblingToDelete is IHasXmlNode)
606 end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
607 // After removal, it moves to parent node.
608 if (!navigator.MoveToParent ())
609 throw new InvalidOperationException ("There is no parent to remove current node.");
611 if (end == null || start.ParentNode != end.ParentNode)
612 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
614 XmlNode parent = start.ParentNode;
617 for (XmlNode n = start; loop; n = next) {
619 next = n.NextSibling;
620 parent.RemoveChild (n);
624 public override XmlWriter ReplaceRange (XPathNavigator nav)
627 throw new ArgumentNullException ();
629 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
631 if (nav is IHasXmlNode)
632 end = ((IHasXmlNode) nav).GetNode ();
633 if (end == null || start.ParentNode != end.ParentNode)
634 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
636 XmlDocumentInsertionWriter w =
637 (XmlDocumentInsertionWriter) InsertBefore ();
639 // local variables to anonymous delegate
640 XPathNavigator prev = Clone ();
641 if (!prev.MoveToPrevious ())
643 XPathNavigator parentNav = Clone ();
644 parentNav.MoveToParent ();
646 w.Closed += delegate (XmlWriter writer) {
647 XmlNode parent = start.ParentNode;
650 for (XmlNode n = start; loop; n = next) {
652 next = n.NextSibling;
653 parent.RemoveChild (n);
667 public override XmlWriter InsertBefore ()
669 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
670 return new XmlDocumentInsertionWriter (n.ParentNode, n);
673 public override XmlWriter CreateAttributes ()
675 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
676 return new XmlDocumentAttributeWriter (n);
679 public override void DeleteSelf ()
681 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
682 XmlAttribute a = n as XmlAttribute;
684 if (a.OwnerElement == null)
685 throw new InvalidOperationException ("This attribute node cannot be removed since it has no owner element.");
686 navigator.MoveToParent ();
687 a.OwnerElement.RemoveAttributeNode (a);
689 if (n.ParentNode == null)
690 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
691 navigator.MoveToParent ();
692 n.ParentNode.RemoveChild (n);
696 public override void ReplaceSelf (XmlReader reader)
698 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
699 XmlNode p = n.ParentNode;
701 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
703 bool movenext = false;
704 if (!MoveToPrevious ())
709 XmlDocument doc = p.NodeType == XmlNodeType.Document ?
710 p as XmlDocument : p.OwnerDocument;
712 if (reader.ReadState == ReadState.Initial) {
718 p.AppendChild (doc.ReadNode (reader));
723 p.AppendChild (doc.ReadNode (reader));
726 throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
736 public override void SetValue (string value)
738 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
739 while (n.FirstChild != null)
740 n.RemoveChild (n.FirstChild);
744 public override void MoveToRoot ()
746 navigator.MoveToRoot ();
749 public override bool MoveToNamespace (string name)
751 return navigator.MoveToNamespace (name);
754 public override bool MoveToFirst ()
756 return navigator.MoveToFirst ();
759 public override bool MoveToAttribute (string localName, string namespaceURI)
761 return navigator.MoveToAttribute (localName, namespaceURI);
764 public override bool IsDescendant (XPathNavigator nav)
766 XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
768 return navigator.IsDescendant (e.navigator);
770 return navigator.IsDescendant (nav);
773 public override string GetNamespace (string name)
775 return navigator.GetNamespace (name);
778 public override string GetAttribute (string localName, string namespaceURI)
780 return navigator.GetAttribute (localName, namespaceURI);
783 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
785 XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
787 return navigator.ComparePosition (e.navigator);
789 return navigator.ComparePosition (nav);