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 XmlReader reader = new XmlTextReader(new System.IO.StringReader(raw));
213 private void WriteRaw(XmlReader reader)
215 if (reader != null && reader.NodeType == XmlNodeType.Element)
217 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
218 WriteAttributes (reader, true);
219 WriteRaw (reader.ReadSubtree ());
225 public override void WriteSurrogateCharEntity (char msb, char lsb)
227 throw new NotSupportedException ();
230 public override void WriteCharEntity (char c)
232 throw new NotSupportedException ();
235 public override void WriteEntityRef (string entname)
237 if (state != WriteState.Attribute)
238 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
239 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
242 public override void WriteChars (char [] data, int start, int length)
244 WriteString (new string (data, start, length));
247 public override void WriteString (string text)
249 if (attribute != null)
250 attribute.Value += text;
252 XmlText t = current.OwnerDocument.CreateTextNode (text);
253 current.AppendChild (t);
257 public override void WriteWhitespace (string text)
259 if (state != WriteState.Attribute)
260 current.AppendChild (current.OwnerDocument.CreateTextNode (text));
261 else if (attribute.ChildNodes.Count == 0)
262 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
264 attribute.Value += text;
267 public override void WriteEndAttribute ()
269 // when the writer is for AppendChild() and the root
270 // node is element, it allows to write attributes
271 // (IMHO incorrectly: isn't it append "child" ???)
272 XmlElement element = (current as XmlElement) ?? (nextSibling == null ? parent as XmlElement : null);
273 if (state != WriteState.Attribute || element == null)
274 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
275 element.SetAttributeNode (attribute);
277 state = WriteState.Content;
281 internal class XmlDocumentAttributeWriter : XmlWriter
285 XmlAttribute attribute;
287 public XmlDocumentAttributeWriter (XmlNode owner)
289 element = owner as XmlElement;
291 throw new ArgumentException ("To write attributes, current node must be an element.");
292 state = WriteState.Content;
295 public override WriteState WriteState {
296 get { return state; }
299 public override void Close ()
303 public override void Flush ()
307 public override string LookupPrefix (string ns)
309 return element.GetPrefixOfNamespace (ns);
312 public override void WriteStartAttribute (string prefix, string name, string ns)
314 if (state != WriteState.Content)
315 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
316 if (prefix == null && ns != null && ns.Length > 0)
317 prefix = LookupPrefix (ns);
318 attribute = element.OwnerDocument.CreateAttribute (prefix, name, ns);
319 state = WriteState.Attribute;
322 public override void WriteProcessingInstruction (string name, string value)
324 throw new NotSupportedException ();
327 public override void WriteComment (string text)
329 throw new NotSupportedException ();
332 public override void WriteCData (string text)
334 throw new NotSupportedException ();
337 public override void WriteStartElement (string prefix, string name, string ns)
339 throw new NotSupportedException ();
342 public override void WriteEndElement ()
344 throw new NotSupportedException ();
347 public override void WriteFullEndElement ()
349 throw new NotSupportedException ();
352 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
354 throw new NotSupportedException ();
357 public override void WriteStartDocument ()
359 throw new NotSupportedException ();
362 public override void WriteStartDocument (bool standalone)
364 throw new NotSupportedException ();
367 public override void WriteEndDocument ()
369 throw new NotSupportedException ();
372 public override void WriteBase64 (byte [] data, int start, int length)
374 throw new NotSupportedException ();
377 public override void WriteRaw (char [] raw, int start, int length)
379 throw new NotSupportedException ();
382 public override void WriteRaw (string raw)
384 throw new NotSupportedException ();
387 public override void WriteSurrogateCharEntity (char msb, char lsb)
389 throw new NotSupportedException ();
392 public override void WriteCharEntity (char c)
394 throw new NotSupportedException ();
397 public override void WriteEntityRef (string entname)
399 if (state != WriteState.Attribute)
400 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
401 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
404 public override void WriteChars (char [] data, int start, int length)
406 WriteString (new string (data, start, length));
409 public override void WriteString (string text)
411 if (state != WriteState.Attribute)
412 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
413 attribute.Value += text;
416 public override void WriteWhitespace (string text)
418 if (state != WriteState.Attribute)
419 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
420 if (attribute.ChildNodes.Count == 0)
421 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
423 attribute.Value += text;
426 public override void WriteEndAttribute ()
428 if (state != WriteState.Attribute)
429 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
430 element.SetAttributeNode (attribute);
432 state = WriteState.Content;
436 internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
438 static readonly bool isXmlDocumentNavigatorImpl;
440 static XmlDocumentEditableNavigator ()
442 isXmlDocumentNavigatorImpl =
443 (typeof (XmlDocumentEditableNavigator).Assembly
444 == typeof (XmlDocument).Assembly);
447 XPathEditableDocument document;
448 XPathNavigator navigator;
450 public XmlDocumentEditableNavigator (XPathEditableDocument doc)
453 if (isXmlDocumentNavigatorImpl)
454 navigator = new XmlDocumentNavigator (doc.Node);
456 navigator = doc.CreateNavigator ();
459 public XmlDocumentEditableNavigator (XmlDocumentEditableNavigator nav)
461 document = nav.document;
462 navigator = nav.navigator.Clone ();
465 public override string BaseURI {
466 get { return navigator.BaseURI; }
469 public override bool CanEdit {
473 public override bool IsEmptyElement {
474 get { return navigator.IsEmptyElement; }
477 public override string LocalName {
478 get { return navigator.LocalName; }
481 public override XmlNameTable NameTable {
482 get { return navigator.NameTable; }
485 public override string Name {
486 get { return navigator.Name; }
489 public override string NamespaceURI {
490 get { return navigator.NamespaceURI; }
493 public override XPathNodeType NodeType {
494 get { return navigator.NodeType; }
497 public override string Prefix {
498 get { return navigator.Prefix; }
501 public override IXmlSchemaInfo SchemaInfo {
502 get { return navigator.SchemaInfo; }
505 public override object UnderlyingObject {
506 get { return navigator.UnderlyingObject; }
509 public override string Value {
510 get { return navigator.Value; }
513 public override string XmlLang {
514 get { return navigator.XmlLang; }
517 public override bool HasChildren {
518 get { return navigator.HasChildren; }
521 public override bool HasAttributes {
522 get { return navigator.HasAttributes; }
525 public override XPathNavigator Clone ()
527 return new XmlDocumentEditableNavigator (this);
530 public override XPathNavigator CreateNavigator ()
532 return navigator.Clone ();
535 public XmlNode GetNode ()
537 return ((IHasXmlNode) navigator).GetNode ();
540 public override bool IsSamePosition (XPathNavigator other)
542 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
544 return navigator.IsSamePosition (nav.navigator);
546 return navigator.IsSamePosition (nav);
549 public override bool MoveTo (XPathNavigator other)
551 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
553 return navigator.MoveTo (nav.navigator);
555 return navigator.MoveTo (nav);
558 public override bool MoveToFirstAttribute ()
560 return navigator.MoveToFirstAttribute ();
563 public override bool MoveToFirstChild ()
565 return navigator.MoveToFirstChild ();
568 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
570 return navigator.MoveToFirstNamespace (scope);
573 public override bool MoveToId (string id)
575 return navigator.MoveToId (id);
578 public override bool MoveToNext ()
580 return navigator.MoveToNext ();
583 public override bool MoveToNextAttribute ()
585 return navigator.MoveToNextAttribute ();
588 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
590 return navigator.MoveToNextNamespace (scope);
593 public override bool MoveToParent ()
595 return navigator.MoveToParent ();
598 public override bool MoveToPrevious ()
600 return navigator.MoveToPrevious ();
603 public override XmlWriter AppendChild ()
605 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
607 throw new InvalidOperationException ("Should not happen.");
608 return new XmlDocumentInsertionWriter (n, null);
611 public override void DeleteRange (XPathNavigator lastSiblingToDelete)
613 if (lastSiblingToDelete == null)
614 throw new ArgumentNullException ();
616 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
618 if (lastSiblingToDelete is IHasXmlNode)
619 end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
620 // After removal, it moves to parent node.
621 if (!navigator.MoveToParent ())
622 throw new InvalidOperationException ("There is no parent to remove current node.");
624 if (end == null || start.ParentNode != end.ParentNode)
625 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
627 XmlNode parent = start.ParentNode;
630 for (XmlNode n = start; loop; n = next) {
632 next = n.NextSibling;
633 parent.RemoveChild (n);
637 public override XmlWriter ReplaceRange (XPathNavigator nav)
640 throw new ArgumentNullException ();
642 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
644 if (nav is IHasXmlNode)
645 end = ((IHasXmlNode) nav).GetNode ();
646 if (end == null || start.ParentNode != end.ParentNode)
647 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
649 XmlDocumentInsertionWriter w =
650 (XmlDocumentInsertionWriter) InsertBefore ();
652 // local variables to anonymous delegate
653 XPathNavigator prev = Clone ();
654 if (!prev.MoveToPrevious ())
656 XPathNavigator parentNav = Clone ();
657 parentNav.MoveToParent ();
659 w.Closed += delegate (XmlWriter writer) {
660 XmlNode parent = start.ParentNode;
663 for (XmlNode n = start; loop; n = next) {
665 next = n.NextSibling;
666 parent.RemoveChild (n);
680 public override XmlWriter InsertBefore ()
682 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
683 return new XmlDocumentInsertionWriter (n.ParentNode, n);
686 public override XmlWriter CreateAttributes ()
688 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
689 return new XmlDocumentAttributeWriter (n);
692 public override void DeleteSelf ()
694 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
695 XmlAttribute a = n as XmlAttribute;
697 if (a.OwnerElement == null)
698 throw new InvalidOperationException ("This attribute node cannot be removed since it has no owner element.");
699 navigator.MoveToParent ();
700 a.OwnerElement.RemoveAttributeNode (a);
702 if (n.ParentNode == null)
703 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
704 navigator.MoveToParent ();
705 n.ParentNode.RemoveChild (n);
709 public override void ReplaceSelf (XmlReader reader)
711 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
712 XmlNode p = n.ParentNode;
714 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
716 bool movenext = false;
717 if (!MoveToPrevious ())
722 XmlDocument doc = p.NodeType == XmlNodeType.Document ?
723 p as XmlDocument : p.OwnerDocument;
725 if (reader.ReadState == ReadState.Initial) {
731 p.AppendChild (doc.ReadNode (reader));
736 p.AppendChild (doc.ReadNode (reader));
739 throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
749 public override void SetValue (string value)
751 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
752 while (n.FirstChild != null)
753 n.RemoveChild (n.FirstChild);
757 public override void MoveToRoot ()
759 navigator.MoveToRoot ();
762 public override bool MoveToNamespace (string name)
764 return navigator.MoveToNamespace (name);
767 public override bool MoveToFirst ()
769 return navigator.MoveToFirst ();
772 public override bool MoveToAttribute (string localName, string namespaceURI)
774 return navigator.MoveToAttribute (localName, namespaceURI);
777 public override bool IsDescendant (XPathNavigator nav)
779 XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
781 return navigator.IsDescendant (e.navigator);
783 return navigator.IsDescendant (nav);
786 public override string GetNamespace (string name)
788 return navigator.GetNamespace (name);
791 public override string GetAttribute (string localName, string namespaceURI)
793 return navigator.GetAttribute (localName, namespaceURI);
796 public override XmlNodeOrder ComparePosition (XPathNavigator nav)
798 XmlDocumentEditableNavigator e = nav as XmlDocumentEditableNavigator;
800 return navigator.ComparePosition (e.navigator);
802 return navigator.ComparePosition (nav);