2 // Mono.Xml.XPath.XPathDocument2Editable
5 // Atsushi Enomoto <atsushi@ximian.com>
9 // Yet another implementation of XPathEditableNavigator.
10 // (Even runnable under MS.NET 2.0)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System
.Collections
;
37 using System
.ComponentModel
;
40 using System
.Xml
.Schema
;
41 using System
.Xml
.XPath
;
42 using System
.Xml
.Serialization
;
44 namespace Mono
.Xml
.XPath
49 public static void Main ()
53 XPathDocument2 doc = new XPathDocument2 ();
54 XPathDocument2Editable pd = new XPathDocument2Editable (doc);
55 XPathEditableNavigator nav = pd.CreateEditor ();
56 IChangeTracking xp = pd;
58 XPathDocument doc = new XPathDocument ();
59 XPathEditableNavigator nav = doc.CreateEditor ();
60 IChangeTracking xp = doc;
62 doc.LoadXml ("<root/>");
63 nav.MoveToFirstChild (); // root
64 XmlWriter w = nav.AppendChild ();
65 Console.WriteLine (((IChangeTracking) xp).IsChanged);
66 w.WriteElementString ("foo", "foo_text");
67 w.WriteElementString ("bar", "bar_text");
68 w.WriteStartElement ("hoge");
69 w.WriteAttributeString ("fuga", "fugafuga");
70 w.WriteAttributeString ("unya", "unyaunya");
71 w.WriteFullEndElement ();
74 w = nav.CreateAttributes ();
75 w.WriteStartAttribute ("namara");
76 w.WriteString ("mokera");
77 w.WriteEndAttribute ();
78 w.WriteAttributeString ("beccho", "giccho");
82 nav.MoveToFirstChild ();
83 nav.MoveToFirstChild ();
84 nav.DeleteCurrent (); // delete foo
85 Console.WriteLine (nav.Name);
87 Console.WriteLine (nav.Name);
88 Console.WriteLine (nav.MoveToFirstAttribute ());
89 nav.DeleteCurrent (); // delete fuga
91 doc.Save (Console.Out);
92 } catch (Exception ex) {
93 Console.WriteLine (ex);
99 public class XPathDocument2Editable
100 : IXPathNavigable
, IXPathEditable
,
101 IRevertibleChangeTracking
, IChangeTracking
, IXmlSerializable
104 XPathDocument2 document
;
106 ArrayList changes
= new ArrayList ();
107 bool enableChangeTracking
;
109 public XPathDocument2Editable (XPathDocument2 doc
)
116 public event NodeChangedEventHandler ChangeRejected
;
118 public event NodeChangedEventHandler ItemUpdated
;
120 public event NodeChangedEventHandler ItemUpdating
;
122 public event NodeChangedEventHandler ItemInserted
;
124 public event NodeChangedEventHandler ItemInserting
;
126 public event NodeChangedEventHandler ItemDeleted
;
128 public event NodeChangedEventHandler ItemDeleting
;
130 public event NodeChangedEventHandler RejectingChange
;
134 public XmlNameTable NameTable
{
135 get { return document.NameTable; }
138 public XPathNavigator
CreateNavigator ()
140 return document
.CreateNavigator ();
143 public XPathEditableNavigator
CreateEditor ()
145 return new XomEditableNavigator (this);
148 public XmlWriter
CreateWriter ()
150 return CreateEditor ().AppendChild ();
153 public bool HasChanges ()
158 public bool EnableChangeTracking
{
159 get { return enableChangeTracking; }
160 set { enableChangeTracking = value; }
163 #region IRevertibleChangeTracking/IChangeTracking
164 public bool IsChanged
{
165 get { return changes.Count != 0; }
168 public void AcceptChanges ()
173 public void RejectChanges ()
175 for (int i
= changes
.Count
- 1; i
>= 0; i
--) {
176 Insertion2 ins
= changes
[i
] as Insertion2
;
178 ins
.ParentNode
.RemoveChild (ins
.InsertedNode
);
182 Removal2 rem
= changes
[i
] as Removal2
;
184 if (rem
.RemovedNode
.NodeType
== XPathNodeType
.Attribute
) {
185 XomElement el
= (XomElement
) rem
.OwnerNode
;
186 el
.AppendAttribute ((XomAttribute
) rem
.RemovedNode
);
189 rem
.OwnerNode
.InsertBefore (rem
.RemovedNode
, rem
.NextSibling
);
192 AttributeUpdate2 au
= changes
[i
] as AttributeUpdate2
;
194 au
.Element
.RemoveAttribute (au
.NewAttribute
);
195 if (au
.OldAttribute
!= null)
196 au
.Element
.AppendAttribute (au
.OldAttribute
);
204 #region IXmlSerializable
205 public void WriteXml (XmlWriter writer
)
207 throw new NotImplementedException ();
210 public void ReadXml (XmlReader reader
)
212 throw new NotImplementedException ();
215 public XmlSchema
GetSchema ()
217 throw new NotImplementedException ();
221 internal bool DeleteNode (XomNode node
)
223 Removal2 rem
= new Removal2 ();
224 if (node
.NodeType
== XPathNodeType
.Attribute
) {
225 XomAttribute attr
= node
as XomAttribute
;
226 rem
.OwnerNode
= attr
.Parent
;
227 rem
.RemovedNode
= node
;
228 ((XomElement
) attr
.Parent
).RemoveAttribute (attr
);
231 rem
.OwnerNode
= node
.Parent
;
232 rem
.NextSibling
= node
.NextSibling
;
233 rem
.RemovedNode
= node
;
234 node
.Parent
.RemoveChild (node
);
235 return rem
.NextSibling
!= null;
239 internal XmlWriter
CreateInsertionWriter (XomNode owner
, XomNode previousSibling
)
241 return new XPathDocument2InsertionWriter (owner
, previousSibling
, this);
244 internal XmlWriter
CreateAttributesWriter (XomNode owner
)
246 return new XPathDocument2AttributeWriter (owner
, this);
249 internal void AttributeUpdate2 (XomElement element
, XomAttribute oldAttr
, XomAttribute newAttr
)
251 AttributeUpdate2 au
= new AttributeUpdate2 ();
252 au
.Element
= element
;
253 au
.OldAttribute
= oldAttr
;
254 au
.NewAttribute
= newAttr
;
258 internal void AppendChild (XomParentNode parent
, XomNode child
)
260 Insertion2 ins
= new Insertion2 ();
261 ins
.ParentNode
= parent
;
262 ins
.InsertedNode
= child
;
267 public class XPathDocument2InsertionWriter
: XmlWriter
270 XomNode previousSibling
;
271 XPathDocument2Editable document
;
272 Stack nodeStack
= new Stack ();
274 public XPathDocument2InsertionWriter (XomNode owner
, XomNode previousSibling
, XPathDocument2Editable doc
)
276 this.current
= (XomNode
) owner
;
278 throw new InvalidOperationException ();
279 this.previousSibling
= previousSibling
;
281 state
= WriteState
.Content
;
285 XomAttribute attribute
;
287 public override WriteState WriteState
{
288 get { return state; }
291 public override void Close ()
295 public override void Flush ()
299 public override string LookupPrefix (string ns
)
301 return current
.LookupPrefix (ns
);
304 public override void WriteStartAttribute (string prefix
, string name
, string ns
)
306 if (state
!= WriteState
.Content
)
307 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
308 attribute
= new XomAttribute (prefix
, name
, ns
, String
.Empty
);
309 state
= WriteState
.Attribute
;
312 public override void WriteProcessingInstruction (string name
, string value)
314 XomParentNode p
= current
as XomParentNode
;
316 throw new InvalidOperationException ("Current writer node cannot have a child.");
317 XomPI pi
= new XomPI (name
, value);
319 document
.AppendChild (p
, pi
);
322 public override void WriteComment (string text
)
324 XomParentNode p
= current
as XomParentNode
;
326 throw new InvalidOperationException ("Current writer node cannot have a child.");
327 XomComment comment
= new XomComment (text
);
328 p
.AppendChild (comment
);
329 document
.AppendChild (p
, comment
);
332 public override void WriteCData (string text
)
334 if (attribute
!= null)
335 throw new InvalidOperationException ("Current writer node is attribute. It cannot accept CDATA section.");
337 XomParentNode p = current as XomParentNode;
339 throw new InvalidOperationException ("Current writer node cannot have a child.");
340 XomText cdata = new XomText (text);
341 p.AppendChild (cdata);
342 document.AppendChild (p, cdata);
347 public override void WriteStartElement (string prefix
, string name
, string ns
)
349 XomParentNode p
= current
as XomParentNode
;
351 throw new InvalidOperationException ("Current writer node cannot have a child.");
352 XomElement el
= new XomElement (prefix
, name
, ns
);
354 document
.AppendChild (p
, el
);
355 nodeStack
.Push (current
);
359 public override void WriteEndElement ()
361 WriteFullEndElement ();
362 XomElement el
= current
as XomElement
;
363 if (el
!= null && el
.ChildCount
== 0)
364 el
.SetIsEmpty (true);
367 public override void WriteFullEndElement ()
369 if (nodeStack
.Count
== 0)
370 throw new InvalidOperationException ("No element is opened.");
371 current
= nodeStack
.Pop () as XomNode
;
374 public override void WriteDocType (string name
, string pubid
, string systemId
, string intsubset
)
376 throw new NotSupportedException ();
379 public override void WriteStartDocument ()
381 throw new NotSupportedException ();
384 public override void WriteStartDocument (bool standalone
)
386 throw new NotSupportedException ();
389 public override void WriteEndDocument ()
391 throw new NotSupportedException ();
394 public override void WriteBase64 (byte [] data
, int start
, int length
)
396 WriteString (Convert
.ToBase64String (data
, start
, length
));
399 public override void WriteRaw (char [] raw
, int start
, int length
)
401 throw new NotSupportedException ();
404 public override void WriteRaw (string raw
)
406 throw new NotSupportedException ();
409 public override void WriteSurrogateCharEntity (char msb
, char lsb
)
411 throw new NotSupportedException ();
414 public override void WriteCharEntity (char c
)
416 throw new NotSupportedException ();
419 public override void WriteEntityRef (string entname
)
421 throw new NotSupportedException ();
424 public override void WriteChars (char [] data
, int start
, int length
)
426 WriteString (new string (data
, start
, length
));
429 public override void WriteString (string text
)
431 if (attribute
!= null)
432 attribute
.Value
+= text
;
434 XomParentNode p
= current
as XomParentNode
;
436 throw new InvalidOperationException ("Current writer node cannot have a child.");
438 XomText xt
= new XomText (text
);
440 document
.AppendChild (p
, xt
);
444 public override void WriteWhitespace (string text
)
446 if (attribute
!= null)
447 attribute
.Value
+= text
;
449 XomParentNode p
= current
as XomParentNode
;
451 throw new InvalidOperationException ("Current writer node cannot have a child.");
453 XomWhitespace ws
= new XomWhitespace (text
);
455 document
.AppendChild (p
, ws
);
459 public override void WriteEndAttribute ()
461 XomElement element
= current
as XomElement
;
462 if (state
!= WriteState
.Attribute
|| element
== null)
463 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
464 XomAttribute old
= element
.GetAttribute (attribute
.LocalName
, attribute
.Namespace
);
465 element
.AppendAttribute (attribute
);
466 document
.AttributeUpdate2 (element
, old
, attribute
);
468 state
= WriteState
.Content
;
472 public class XPathDocument2AttributeWriter
: XmlWriter
475 XPathDocument2Editable document
;
477 public XPathDocument2AttributeWriter (XomNode owner
, XPathDocument2Editable doc
)
479 element
= owner
as XomElement
;
481 throw new ArgumentException ("To write attributes, current node must be an element.");
482 state
= WriteState
.Content
;
487 XomAttribute attribute
;
489 public override WriteState WriteState
{
490 get { return state; }
493 public override void Close ()
497 public override void Flush ()
501 public override string LookupPrefix (string ns
)
503 return element
.LookupPrefix (ns
);
506 public override void WriteStartAttribute (string prefix
, string name
, string ns
)
508 if (state
!= WriteState
.Content
)
509 throw new InvalidOperationException ("Current state is not inside element. Cannot start attribute.");
510 attribute
= new XomAttribute (prefix
, name
, ns
, String
.Empty
);
511 state
= WriteState
.Attribute
;
514 public override void WriteProcessingInstruction (string name
, string value)
516 throw new NotSupportedException ();
519 public override void WriteComment (string text
)
521 throw new NotSupportedException ();
524 public override void WriteCData (string text
)
526 throw new NotSupportedException ();
529 public override void WriteStartElement (string prefix
, string name
, string ns
)
531 throw new NotSupportedException ();
534 public override void WriteEndElement ()
536 throw new NotSupportedException ();
539 public override void WriteFullEndElement ()
541 throw new NotSupportedException ();
544 public override void WriteDocType (string name
, string pubid
, string systemId
, string intsubset
)
546 throw new NotSupportedException ();
549 public override void WriteStartDocument ()
551 throw new NotSupportedException ();
554 public override void WriteStartDocument (bool standalone
)
556 throw new NotSupportedException ();
559 public override void WriteEndDocument ()
561 throw new NotSupportedException ();
564 public override void WriteBase64 (byte [] data
, int start
, int length
)
566 throw new NotSupportedException ();
569 public override void WriteRaw (char [] raw
, int start
, int length
)
571 throw new NotSupportedException ();
574 public override void WriteRaw (string raw
)
576 throw new NotSupportedException ();
579 public override void WriteSurrogateCharEntity (char msb
, char lsb
)
581 throw new NotSupportedException ();
584 public override void WriteCharEntity (char c
)
586 throw new NotSupportedException ();
589 public override void WriteEntityRef (string entname
)
591 throw new NotSupportedException ();
594 public override void WriteChars (char [] data
, int start
, int length
)
596 WriteString (new string (data
, start
, length
));
599 public override void WriteString (string text
)
601 if (state
!= WriteState
.Attribute
)
602 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
603 attribute
.Value
+= text
;
606 public override void WriteWhitespace (string text
)
608 if (state
!= WriteState
.Attribute
)
609 throw new InvalidOperationException ("Current state is not inside attribute. Cannot write attribute value.");
611 attribute
.Value
+= text
;
614 public override void WriteEndAttribute ()
616 if (state
!= WriteState
.Attribute
)
617 throw new InvalidOperationException ("Current state is not inside attribute. Cannot close attribute.");
618 XomAttribute old
= element
.GetAttribute (attribute
.LocalName
, attribute
.Namespace
);
619 element
.AppendAttribute (attribute
);
620 document
.AttributeUpdate2 (element
, old
, attribute
);
622 state
= WriteState
.Content
;
626 public class Insertion2
628 // AppendChild : last child / true
629 // InsertBefore : current node / false
630 // InsertAfter : current node / true
631 // PrependChild : first child / false
632 public XomParentNode ParentNode
;
633 public XomNode InsertedNode
;
634 public bool Afterward
;
637 public class Removal2
639 public XomParentNode OwnerNode
;
640 public XomNode NextSibling
;
641 public XomNode RemovedNode
;
644 public class AttributeUpdate2
646 public XomElement Element
;
647 public XomAttribute NewAttribute
;
648 public XomAttribute OldAttribute
;
651 public class XomEditableNavigator
: XPathEditableNavigator
, IHasXomNode
653 XPathDocument2Editable document
;
654 XPathNavigator navigator
;
656 public XomEditableNavigator (XPathDocument2Editable doc
)
659 navigator
= doc
.CreateNavigator ();
662 public XomEditableNavigator (XomEditableNavigator nav
)
664 document
= nav
.document
;
665 navigator
= nav
.navigator
.Clone ();
668 public override string BaseURI
{
669 get { return navigator.BaseURI; }
672 public override bool IsEmptyElement
{
673 get { return navigator.IsEmptyElement; }
676 public override string LocalName
{
677 get { return navigator.LocalName; }
680 public override XmlNameTable NameTable
{
681 get { return navigator.NameTable; }
684 public override string Name
{
685 get { return navigator.Name; }
688 public override string NamespaceURI
{
689 get { return navigator.NamespaceURI; }
692 public override XPathNodeType NodeType
{
693 get { return navigator.NodeType; }
696 public override string Prefix
{
697 get { return navigator.Prefix; }
700 public override string Value
{
701 get { return navigator.Value; }
704 public override XPathNavigator
Clone ()
706 return new XomEditableNavigator (this);
709 public override XPathNavigator
CreateNavigator ()
711 return navigator
.Clone ();
714 public XomNode
GetNode ()
716 return ((IHasXomNode
) navigator
).GetNode ();
719 public override bool IsSamePosition (XPathNavigator other
)
721 XomEditableNavigator nav
= other
as XomEditableNavigator
;
723 return navigator
.IsSamePosition (nav
.navigator
);
725 return navigator
.IsSamePosition (nav
);
728 public override bool MoveTo (XPathNavigator other
)
730 XomEditableNavigator nav
= other
as XomEditableNavigator
;
732 return navigator
.MoveTo (nav
.navigator
);
734 return navigator
.MoveTo (nav
);
737 public override bool MoveToFirstAttribute ()
739 return navigator
.MoveToFirstAttribute ();
742 public override bool MoveToFirstChild ()
744 return navigator
.MoveToFirstChild ();
747 public override bool MoveToFirstNamespace (XPathNamespaceScope scope
)
749 return navigator
.MoveToFirstNamespace (scope
);
752 public override bool MoveToId (string id
)
754 return navigator
.MoveToId (id
);
757 public override bool MoveToNext ()
759 return navigator
.MoveToNext ();
762 public override bool MoveToNextAttribute ()
764 return navigator
.MoveToNextAttribute ();
767 public override bool MoveToNextNamespace (XPathNamespaceScope scope
)
769 return navigator
.MoveToNextNamespace (scope
);
772 public override bool MoveToParent ()
774 return navigator
.MoveToParent ();
777 public override bool MoveToPrevious ()
779 return navigator
.MoveToPrevious ();
782 public override void Validate (XmlSchemaSet schemas
, ValidationEventHandler handler
)
784 throw new NotImplementedException ();
786 // FIXME: use handler
787 XmlReaderSettings settings = new XmlReaderSettings ();
788 settings.Schemas.Add (schemas);
789 settings.NameTable = this.NameTable;
790 settings.XsdValidate = true;
791 settings.DtdValidate = false;
792 XmlReader xvr = XmlReader.Create (new XPathNavigatorReader (this), settings);
798 public override XmlWriter
AppendChild ()
800 XomNode n
= ((IHasXomNode
) navigator
).GetNode ();
802 throw new InvalidOperationException ("Should not happen.");
803 return document
.CreateInsertionWriter (n
, null);
806 public override XmlWriter
InsertBefore ()
808 XomNode n
= ((IHasXomNode
) navigator
).GetNode ();
809 return document
.CreateInsertionWriter (n
.Parent
, n
.PreviousSibling
);
812 public override XmlWriter
CreateAttributes ()
814 XomNode n
= ((IHasXomNode
) navigator
).GetNode ();
815 return document
.CreateAttributesWriter (n
);
818 public override bool DeleteCurrent ()
820 XomNode n
= ((IHasXomNode
) navigator
).GetNode ();
821 if (!navigator
.MoveToNext ())
822 navigator
.MoveToParent ();
823 return document
.DeleteNode (n
);
826 public override void SetValue (object value)
828 XomNode n
= ((IHasXomNode
) navigator
).GetNode ();
829 int count
= n
.ChildCount
;
830 while (n
.FirstChild
!= null)
831 document
.DeleteNode (n
.FirstChild
);
832 XmlWriter w
= document
.CreateInsertionWriter (n
, null);
833 w
.WriteValue (value);