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.DocumentFragment:
89 case XmlNodeType.Element:
90 current = parent.OwnerDocument.CreateDocumentFragment ();
93 throw new InvalidOperationException (String.Format ("Insertion into {0} node is not allowed.", parent.NodeType));
95 this.nextSibling = nextSibling;
96 state = WriteState.Content;
100 XmlAttribute attribute;
102 public override WriteState WriteState {
103 get { return state; }
106 public override void Close ()
108 while (nodeStack.Count > 0) {
109 XmlNode n = nodeStack.Pop () as XmlNode;
110 n.AppendChild (current);
113 parent.InsertBefore ((XmlDocumentFragment) current, nextSibling);
118 internal event XmlWriterClosedEventHandler Closed;
120 internal XmlNode AppendedFirstChild;
122 public override void Flush ()
126 public override string LookupPrefix (string ns)
128 return current.GetPrefixOfNamespace (ns);
131 public override void WriteStartAttribute (string prefix, string name, string ns)
133 if (state != WriteState.Content)
134 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
135 attribute = current.OwnerDocument.CreateAttribute (prefix, name, ns);
136 state = WriteState.Attribute;
139 public override void WriteProcessingInstruction (string name, string value)
141 XmlProcessingInstruction pi = current.OwnerDocument.CreateProcessingInstruction (name, value);
142 current.AppendChild (pi);
145 public override void WriteComment (string text)
147 XmlComment comment = current.OwnerDocument.CreateComment (text);
148 current.AppendChild (comment);
151 public override void WriteCData (string text)
153 XmlCDataSection cdata = current.OwnerDocument.CreateCDataSection (text);
154 current.AppendChild (cdata);
157 public override void WriteStartElement (string prefix, string name, string ns)
159 XmlElement el = current.OwnerDocument.CreateElement (prefix, name, ns);
160 current.AppendChild (el);
161 nodeStack.Push (current);
165 public override void WriteEndElement ()
167 if (nodeStack.Count == 0)
168 throw new InvalidOperationException ("No element is opened.");
169 current = nodeStack.Pop () as XmlNode;
172 public override void WriteFullEndElement ()
174 XmlElement el = current as XmlElement;
180 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
182 throw new NotSupportedException ();
185 public override void WriteStartDocument ()
187 throw new NotSupportedException ();
190 public override void WriteStartDocument (bool standalone)
192 throw new NotSupportedException ();
195 public override void WriteEndDocument ()
197 throw new NotSupportedException ();
200 public override void WriteBase64 (byte [] data, int start, int length)
202 WriteString (Convert.ToBase64String (data, start, length));
205 public override void WriteRaw (char [] raw, int start, int length)
207 throw new NotSupportedException ();
210 public override void WriteRaw (string raw)
212 throw new NotSupportedException ();
215 public override void WriteSurrogateCharEntity (char msb, char lsb)
217 throw new NotSupportedException ();
220 public override void WriteCharEntity (char c)
222 throw new NotSupportedException ();
225 public override void WriteEntityRef (string entname)
227 if (state != WriteState.Attribute)
228 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
229 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
232 public override void WriteChars (char [] data, int start, int length)
234 WriteString (new string (data, start, length));
237 public override void WriteString (string text)
239 if (attribute != null)
240 attribute.Value += text;
242 XmlText t = current.OwnerDocument.CreateTextNode (text);
243 current.AppendChild (t);
247 public override void WriteWhitespace (string text)
249 if (state != WriteState.Attribute)
250 current.AppendChild (current.OwnerDocument.CreateTextNode (text));
251 else if (attribute.ChildNodes.Count == 0)
252 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
254 attribute.Value += text;
257 public override void WriteEndAttribute ()
259 XmlElement element = current as XmlElement;
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 public XmlDocumentAttributeWriter (XmlNode owner)
274 element = owner as XmlElement;
276 throw new ArgumentException ("To write attributes, current node must be an element.");
277 state = WriteState.Content;
281 XmlAttribute attribute;
283 public override WriteState WriteState {
284 get { return state; }
287 public override void Close ()
291 public override void Flush ()
295 public override string LookupPrefix (string ns)
297 return element.GetPrefixOfNamespace (ns);
300 public override void WriteStartAttribute (string prefix, string name, string ns)
302 if (state != WriteState.Content)
303 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
304 attribute = element.OwnerDocument.CreateAttribute (prefix, name, ns);
305 state = WriteState.Attribute;
308 public override void WriteProcessingInstruction (string name, string value)
310 throw new NotSupportedException ();
313 public override void WriteComment (string text)
315 throw new NotSupportedException ();
318 public override void WriteCData (string text)
320 throw new NotSupportedException ();
323 public override void WriteStartElement (string prefix, string name, string ns)
325 throw new NotSupportedException ();
328 public override void WriteEndElement ()
330 throw new NotSupportedException ();
333 public override void WriteFullEndElement ()
335 throw new NotSupportedException ();
338 public override void WriteDocType (string name, string pubid, string systemId, string intsubset)
340 throw new NotSupportedException ();
343 public override void WriteStartDocument ()
345 throw new NotSupportedException ();
348 public override void WriteStartDocument (bool standalone)
350 throw new NotSupportedException ();
353 public override void WriteEndDocument ()
355 throw new NotSupportedException ();
358 public override void WriteBase64 (byte [] data, int start, int length)
360 throw new NotSupportedException ();
363 public override void WriteRaw (char [] raw, int start, int length)
365 throw new NotSupportedException ();
368 public override void WriteRaw (string raw)
370 throw new NotSupportedException ();
373 public override void WriteSurrogateCharEntity (char msb, char lsb)
375 throw new NotSupportedException ();
378 public override void WriteCharEntity (char c)
380 throw new NotSupportedException ();
383 public override void WriteEntityRef (string entname)
385 if (state != WriteState.Attribute)
386 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
387 attribute.AppendChild (attribute.OwnerDocument.CreateEntityReference (entname));
390 public override void WriteChars (char [] data, int start, int length)
392 WriteString (new string (data, start, length));
395 public override void WriteString (string text)
397 if (state != WriteState.Attribute)
398 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
399 attribute.Value += text;
402 public override void WriteWhitespace (string text)
404 if (state != WriteState.Attribute)
405 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
406 if (attribute.ChildNodes.Count == 0)
407 attribute.AppendChild (attribute.OwnerDocument.CreateWhitespace (text));
409 attribute.Value += text;
412 public override void WriteEndAttribute ()
414 if (state != WriteState.Attribute)
415 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
416 element.SetAttributeNode (attribute);
418 state = WriteState.Content;
422 internal class XmlDocumentEditableNavigator : XPathNavigator, IHasXmlNode
424 static readonly bool isXmlDocumentNavigatorImpl;
426 static XmlDocumentEditableNavigator ()
428 isXmlDocumentNavigatorImpl =
429 (typeof (XmlDocumentEditableNavigator).Assembly
430 == typeof (XmlDocument).Assembly);
433 XPathEditableDocument document;
434 XPathNavigator navigator;
436 public XmlDocumentEditableNavigator (XPathEditableDocument doc)
439 if (isXmlDocumentNavigatorImpl)
440 navigator = new XmlDocumentNavigator (doc.Node);
442 navigator = doc.CreateNavigator ();
445 public XmlDocumentEditableNavigator (XmlDocumentEditableNavigator nav)
447 document = nav.document;
448 navigator = nav.navigator.Clone ();
451 public override string BaseURI {
452 get { return navigator.BaseURI; }
455 public override bool CanEdit {
459 public override bool IsEmptyElement {
460 get { return navigator.IsEmptyElement; }
463 public override string LocalName {
464 get { return navigator.LocalName; }
467 public override XmlNameTable NameTable {
468 get { return navigator.NameTable; }
471 public override string Name {
472 get { return navigator.Name; }
475 public override string NamespaceURI {
476 get { return navigator.NamespaceURI; }
479 public override XPathNodeType NodeType {
480 get { return navigator.NodeType; }
483 public override string Prefix {
484 get { return navigator.Prefix; }
487 public override IXmlSchemaInfo SchemaInfo {
488 get { return navigator.SchemaInfo; }
491 public override object UnderlyingObject {
492 get { return navigator.UnderlyingObject; }
495 public override string Value {
496 get { return navigator.Value; }
499 public override XPathNavigator Clone ()
501 return new XmlDocumentEditableNavigator (this);
504 public override XPathNavigator CreateNavigator ()
506 return navigator.Clone ();
509 public XmlNode GetNode ()
511 return ((IHasXmlNode) navigator).GetNode ();
514 public override bool IsSamePosition (XPathNavigator other)
516 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
518 return navigator.IsSamePosition (nav.navigator);
520 return navigator.IsSamePosition (nav);
523 public override bool MoveTo (XPathNavigator other)
525 XmlDocumentEditableNavigator nav = other as XmlDocumentEditableNavigator;
527 return navigator.MoveTo (nav.navigator);
529 return navigator.MoveTo (nav);
532 public override bool MoveToFirstAttribute ()
534 return navigator.MoveToFirstAttribute ();
537 public override bool MoveToFirstChild ()
539 return navigator.MoveToFirstChild ();
542 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
544 return navigator.MoveToFirstNamespace (scope);
547 public override bool MoveToId (string id)
549 return navigator.MoveToId (id);
552 public override bool MoveToNext ()
554 return navigator.MoveToNext ();
557 public override bool MoveToNextAttribute ()
559 return navigator.MoveToNextAttribute ();
562 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
564 return navigator.MoveToNextNamespace (scope);
567 public override bool MoveToParent ()
569 return navigator.MoveToParent ();
572 public override bool MoveToPrevious ()
574 return navigator.MoveToPrevious ();
577 public override XmlWriter AppendChild ()
579 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
581 throw new InvalidOperationException ("Should not happen.");
582 return new XmlDocumentInsertionWriter (n, null);
585 public override void DeleteRange (XPathNavigator lastSiblingToDelete)
587 if (lastSiblingToDelete == null)
588 throw new ArgumentNullException ();
590 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
592 if (lastSiblingToDelete is IHasXmlNode)
593 end = ((IHasXmlNode) lastSiblingToDelete).GetNode ();
594 // After removal, it moves to parent node.
595 if (!navigator.MoveToParent ())
596 throw new InvalidOperationException ("There is no parent to remove current node.");
598 if (end == null || start.ParentNode != end.ParentNode)
599 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
601 XmlNode parent = start.ParentNode;
604 for (XmlNode n = start; loop; n = next) {
606 next = n.NextSibling;
607 parent.RemoveChild (n);
611 public override XmlWriter ReplaceRange (XPathNavigator nav)
614 throw new ArgumentNullException ();
616 XmlNode start = ((IHasXmlNode) navigator).GetNode ();
618 if (nav is IHasXmlNode)
619 end = ((IHasXmlNode) nav).GetNode ();
620 if (end == null || start.ParentNode != end.ParentNode)
621 throw new InvalidOperationException ("Argument XPathNavigator has different parent node.");
623 XmlDocumentInsertionWriter w =
624 (XmlDocumentInsertionWriter) InsertBefore ();
626 // local variables to anonymous delegate
627 XPathNavigator prev = Clone ();
628 if (!prev.MoveToPrevious ())
630 XPathNavigator parentNav = Clone ();
631 parentNav.MoveToParent ();
633 w.Closed += delegate (XmlWriter writer) {
634 XmlNode parent = start.ParentNode;
637 for (XmlNode n = start; loop; n = next) {
639 next = n.NextSibling;
640 parent.RemoveChild (n);
654 public override XmlWriter InsertBefore ()
656 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
657 return new XmlDocumentInsertionWriter (n.ParentNode, n);
660 public override XmlWriter CreateAttributes ()
662 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
663 return new XmlDocumentAttributeWriter (n);
666 public override void DeleteSelf ()
668 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
669 if (!navigator.MoveToNext ())
670 navigator.MoveToParent ();
671 if (n.ParentNode == null)
672 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
673 n.ParentNode.RemoveChild (n);
676 public override void ReplaceSelf (XmlReader reader)
678 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
679 XmlNode p = n.ParentNode;
681 throw new InvalidOperationException ("This node cannot be removed since it has no parent.");
683 bool movenext = false;
684 if (!MoveToPrevious ())
689 XmlDocument doc = p.NodeType == XmlNodeType.Document ?
690 p as XmlDocument : p.OwnerDocument;
692 if (reader.ReadState == ReadState.Initial) {
698 p.AppendChild (doc.ReadNode (reader));
703 p.AppendChild (doc.ReadNode (reader));
706 throw new InvalidOperationException ("Content is required in argument XmlReader to replace current node.");
716 public override void SetValue (string value)
718 XmlNode n = ((IHasXmlNode) navigator).GetNode ();
719 while (n.FirstChild != null)
720 n.RemoveChild (n.FirstChild);