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 string Value {
487 get { return navigator.Value; }
490 public override XPathNavigator Clone ()
492 return new XmlDocumentEditableNavigator (this);
495 public override XPathNavigator CreateNavigator ()
497 return navigator.Clone ();
500 public XmlNode GetNode ()
502 return ((IHasXmlNode) navigator).GetNode ();
505 public override bool IsSamePosition (XPathNavigator other)
507 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
509 return navigator.IsSamePosition (nav.navigator);
511 return navigator.IsSamePosition (nav);
514 public override bool MoveTo (XPathNavigator other)
516 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
518 return navigator.MoveTo (nav.navigator);
520 return navigator.MoveTo (nav);
523 public override bool MoveToFirstAttribute ()
525 return navigator.MoveToFirstAttribute ();
528 public override bool MoveToFirstChild ()
530 return navigator.MoveToFirstChild ();
533 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
535 return navigator.MoveToFirstNamespace (scope);
538 public override bool MoveToId (string id)
540 return navigator.MoveToId (id);
543 public override bool MoveToNext ()
545 return navigator.MoveToNext ();
548 public override bool MoveToNextAttribute ()
550 return navigator.MoveToNextAttribute ();
553 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
555 return navigator.MoveToNextNamespace (scope);
558 public override bool MoveToParent ()
560 return navigator.MoveToParent ();
563 public override bool MoveToPrevious ()
565 return navigator.MoveToPrevious ();
568 public override XmlWriter AppendChild ()
570 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
572 throw new InvalidOperationException ("Should not happen.");
573 return new XmlDocumentInsertionWriter (n, null);
576 public override void DeleteRange (XPathNavigator lastSiblingToDelete)
578 if (lastSiblingToDelete == null)
579 throw new ArgumentNullException ();
581 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
583 if (lastSiblingToDelete is IHasXmlNode)
584 end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
585 // After removal, it moves to parent node.
586 if (!navigator.MoveToParent ())
587 throw new InvalidOperationException ("There is no parent to remove current node.");
589 if (end == null || start.ParentNode != end.ParentNode)
590 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
592 XmlNode parent = start.ParentNode;
595 for (XmlNode n = start; loop; n = next) {
597 next = n.NextSibling;
598 parent.RemoveChild (n);
602 public override XmlWriter ReplaceRange (XPathNavigator nav)
605 throw new ArgumentNullException ();
607 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
609 if (nav is IHasXmlNode)
610 end = ((IHasXmlNode) nav).GetNode ();
611 if (end == null || start.ParentNode != end.ParentNode)
612 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
614 XmlDocumentInsertionWriter w =
615 (XmlDocumentInsertionWriter) InsertBefore ();
617 // local variables to anonymous delegate
618 XPathNavigator prev = Clone ();
619 if (!prev.MoveToPrevious ())
621 XPathNavigator parentNav = Clone ();
622 parentNav.MoveToParent ();
624 w.Closed += delegate (XmlWriter w) {
625 XmlNode parent = start.ParentNode;
628 for (XmlNode n = start; loop; n = next) {
630 next = n.NextSibling;
631 parent.RemoveChild (n);
645 public override XmlWriter InsertBefore ()
647 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
648 return new XmlDocumentInsertionWriter (n.ParentNode, n);
651 public override XmlWriter CreateAttributes ()
653 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
654 return new XmlDocumentAttributeWriter (n);
657 public override void DeleteSelf ()
659 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
660 if (!navigator.MoveToNext ())
661 navigator.MoveToParent ();
662 if (n.ParentNode == null)
663 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
664 n.ParentNode.RemoveChild (n);
667 public override void ReplaceSelf (XmlReader reader)
669 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
670 XmlNode p = n.ParentNode;
672 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
674 bool movenext = false;
675 if (!MoveToPrevious ())
680 XmlDocument doc = p.NodeType == XmlNodeType.Document ?
681 p as XmlDocument : p.OwnerDocument;
683 if (reader.ReadState == ReadState.Initial) {
689 p.AppendChild (doc.ReadNode (reader));
694 p.AppendChild (doc.ReadNode (reader));
697 throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
707 public override void SetValue (string value)
709 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
710 while (n.FirstChild != null)
711 n.RemoveChild (n.FirstChild);