2 // Mono.Xml.XPath.XPathDocument2.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
9 // Another Document tree model.
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
35 using System
.Collections
.Specialized
;
39 using System
.Xml
.XPath
;
41 namespace Mono
.Xml
.XPath
47 public static void Main ()
50 DateTime start = DateTime.Now;
51 Console.WriteLine (DateTime.Now.Ticks);
53 XmlDocument doc = new XmlDocument ();
54 doc.PreserveWhitespace = true;
55 doc.Load (new XmlTextReader ("../TestResult.xml"));
57 XPathDocument2 doc = new XPathDocument2 ();
58 doc.Load (new XmlTextReader ("../TestResult.xml"));
59 // XPathDocument doc = new XPathDocument ("../TestResult.xml", XmlSpace.Preserve);
60 // XPathDocument doc = new XPathDocument ("test.xml", XmlSpace.Preserve);
62 // doc.Load (new XmlTextReader ("test.xml"));
64 // doc.WriteTo (new XmlTextWriter (Console.Out));
66 Console.WriteLine (DateTime.Now.Ticks);
68 XPathNavigator nav = doc.CreateNavigator ();
69 //Console.WriteLine (nav.MoveToFirstChild ());
70 //Console.WriteLine (nav.LocalName + nav.NodeType);
71 //Console.WriteLine (nav.MoveToNext ());
72 //Console.WriteLine (nav.LocalName + nav.NodeType);
73 //Console.WriteLine (nav.MoveToNext ());
74 //Console.WriteLine (nav.LocalName + nav.NodeType);
75 //Console.WriteLine (nav.MoveToNext ());
76 //Console.WriteLine (nav.LocalName + nav.NodeType);
80 XmlReader reader = nav.ReadSubtree ();
81 XmlTextWriter w = new XmlTextWriter (new StringWriter ());
82 // XmlTextWriter w = new XmlTextWriter (Console.Out);
83 w.WriteNode (reader, false);
84 Console.WriteLine (DateTime.Now.Ticks);
85 Console.WriteLine (DateTime.Now.Ticks - start.Ticks);
86 } catch (Exception ex) {
87 Console.WriteLine (ex);
95 public class XPathDocument2
: IXPathNavigable
99 public XPathDocument2 ()
100 : this (new NameTable ())
104 public XPathDocument2 (XmlNameTable nameTable
)
106 root
= new XomRoot (nameTable
);
109 public XmlNameTable NameTable
{
110 get { return root.NameTable; }
113 public void LoadXml (string xml
)
115 Load (new XmlTextReader (xml
, XmlNodeType
.Document
, null));
118 public void Load (TextReader reader
)
120 Load (new XmlTextReader (reader
));
123 public void Load (XmlReader reader
)
125 Load (reader
, XmlSpace
.None
);
128 public void Load (XmlReader reader
, XmlSpace space
)
130 root
.Load (reader
, space
);
133 public void Save (TextWriter writer
)
135 XmlTextWriter xtw
= new XmlTextWriter (writer
);
136 xtw
.Formatting
= Formatting
.Indented
;
140 public void Save (XmlWriter writer
)
145 public void WriteTo (XmlWriter writer
)
147 root
.WriteTo (writer
);
150 public XPathNavigator
CreateNavigator ()
152 return new XomNavigator (root
);
158 public struct XmlName
160 public string Prefix
;
161 public string LocalName
;
162 public string Namespace
;
165 public XmlName (string prefix
, string name
, string ns
)
167 this.Prefix
= prefix
== null ? "" : prefix
;
168 this.LocalName
= name
;
169 this.Namespace
= ns
== null ? "" : ns
;
173 public override bool Equals (object o
)
175 if ( !(o
is XmlName
))
177 XmlName other
= (XmlName
) o
;
178 return LocalName
== other
.LocalName
&& Namespace
== other
.Namespace
;
181 public static bool operator == (XmlName n1
, XmlName n2
)
183 return n1
.LocalName
== n2
.LocalName
&& n1
.Namespace
== n2
.Namespace
;
186 public static bool operator != (XmlName n1
, XmlName n2
)
188 return n1
.LocalName
!= n2
.LocalName
|| n1
.Namespace
!= n2
.Namespace
;
191 public override int GetHashCode ()
193 if (fullName
== null)
194 fullName
= String
.Concat (LocalName
, "/", Namespace
);
195 return fullName
.GetHashCode ();
198 public override string ToString ()
200 if (fullName
== null)
201 fullName
= String
.Concat (LocalName
, "/", Namespace
);
206 public abstract class XomNode
208 XomParentNode parent
;
210 XomNode previousSibling
;
213 public XomRoot Root
{
216 while (n
.parent
!= null)
222 public string PrefixedName
{
224 if (prefixedName
== null) {
225 if (Prefix
.Length
> 0)
226 prefixedName
= Prefix
+ ':' + LocalName
;
228 prefixedName
= LocalName
;
234 public virtual string BaseURI
{
235 get { return Root.BaseURI; }
238 public virtual string XmlLang
{
239 get { return String.Empty; }
242 public XomParentNode Parent
{
243 get { return parent; }
246 public XomNode PreviousSibling
{
247 get { return previousSibling; }
250 public XomNode NextSibling
{
251 get { return nextSibling; }
254 internal void SetParent (XomParentNode parent
)
256 this.parent
= parent
;
259 internal void SetPreviousSibling (XomNode previous
)
261 if (previous
.parent
!= parent
|| this == previous
)
262 throw new InvalidOperationException ();
263 nextSibling
= previous
.nextSibling
;
264 previousSibling
= previous
;
265 previous
.nextSibling
= this;
268 internal void SetNextSibling (XomNode next
)
270 if (next
.parent
!= parent
|| this == next
)
271 throw new InvalidOperationException ();
272 previousSibling
= next
.previousSibling
;
274 next
.previousSibling
= this;
277 internal void RemoveItself ()
279 if (previousSibling
!= null)
280 previousSibling
.nextSibling
= nextSibling
;
281 if (nextSibling
!= null)
282 nextSibling
.previousSibling
= previousSibling
;
286 public string LookupPrefix (string ns
)
288 XomElement n
= this as XomElement
;
290 n
= Parent
as XomElement
;
292 int len
= n
.NamespaceCount
;
293 for (int i
= 0; i
< len
; i
++) {
294 XomNamespace nn
= n
.GetLocalNamespace (i
);
302 public string LookupNamespace (string ns
)
304 XomElement n
= this as XomElement
;
306 n
= Parent
as XomElement
;
308 int len
= n
.NamespaceCount
;
309 for (int i
= 0; i
< len
; i
++) {
310 XomNamespace nn
= n
.GetLocalNamespace (i
);
311 if (nn
.Namespace
== ns
)
318 public virtual bool IsEmptyElement
{
319 get { return false; }
322 public abstract string LocalName { get; }
324 public abstract string Namespace { get; }
326 public abstract string Prefix { get; }
328 public abstract string Value { get; set; }
330 public abstract XPathNodeType NodeType { get; }
332 public abstract int ChildCount { get; }
334 public virtual XomNode FirstChild { get { return null; }
}
336 public virtual XomNode LastChild { get { return null; }
}
338 internal abstract void BuildValue (StringBuilder sb
);
340 public string OuterXml
{
342 StringWriter sw
= new StringWriter ();
343 XmlTextWriter xtw
= new XmlTextWriter (sw
);
345 return sw
.ToString ();
349 public string InnerXml
{
351 StringWriter sw
= new StringWriter ();
352 XmlTextWriter xtw
= new XmlTextWriter (sw
);
353 for (XomNode n
= FirstChild
; n
!= null; n
= n
.NextSibling
)
355 return sw
.ToString ();
359 public abstract void WriteTo (XmlWriter writer
);
362 public interface IHasXomNode
367 public abstract class XomParentNode
: XomNode
373 public void ReadNode (XmlReader reader
, XmlSpace space
)
375 switch (reader
.ReadState
) {
376 case ReadState
.Initial
:
379 case ReadState
.Error
:
380 case ReadState
.Closed
:
381 case ReadState
.EndOfFile
:
382 throw new ArgumentException ("Argument XmlReader is not readable.");
385 switch (reader
.NodeType
) {
386 case XmlNodeType
.Element
:
387 XomElement el
= new XomElement (reader
.Prefix
, reader
.LocalName
, reader
.NamespaceURI
, this);
388 if (reader
.MoveToFirstAttribute ()) {
390 new XomAttribute (reader
.Prefix
, reader
.LocalName
, reader
.NamespaceURI
, reader
.Value
, el
);
391 } while (reader
.MoveToNextAttribute ());
392 reader
.MoveToContent ();
394 if (reader
.IsEmptyElement
) {
395 el
.SetIsEmpty (true);
400 while (reader
.NodeType
!= XmlNodeType
.EndElement
)
401 el
.ReadNode (reader
, space
);
402 reader
.ReadEndElement ();
405 case XmlNodeType
.Text
:
406 case XmlNodeType
.CDATA
:
407 XomText text
= new XomText (reader
.Value
, this);
410 case XmlNodeType
.SignificantWhitespace
:
411 XomSignificantWhitespace sws
= new XomSignificantWhitespace (reader
.Value
, this);
414 case XmlNodeType
.Whitespace
:
415 if (space
== XmlSpace
.Default
) {
419 XomWhitespace ws
= new XomWhitespace (reader
.Value
, this);
422 case XmlNodeType
.ProcessingInstruction
:
423 XomPI pi
= new XomPI (reader
.LocalName
, reader
.Value
, this);
426 case XmlNodeType
.Comment
:
427 XomComment comment
= new XomComment (reader
.Value
, this);
436 public void AppendChild (XomNode child
)
438 InsertBefore (child
, null);
441 public void InsertBefore (XomNode child
, XomNode nextNode
)
443 if (child
.Parent
!= null)
444 throw new InvalidOperationException ("The child already has a parent.");
445 if (nextNode
== null) {
446 child
.SetParent (this);
447 if (firstChild
== null)
448 firstChild
= lastChild
= child
;
450 child
.SetPreviousSibling (lastChild
);
454 if (nextNode
.Parent
!= this)
455 throw new ArgumentException ("Argument nextNode is not a child of this node.");
456 child
.SetNextSibling (nextNode
);
461 public abstract void Clear ();
463 public override XomNode FirstChild
{
464 get { return firstChild; }
467 public override XomNode LastChild
{
468 get { return lastChild; }
471 public override int ChildCount
{
472 get { return childCount; }
475 internal void ClearChildren ()
477 firstChild
= lastChild
= null;
481 public void RemoveChild (XomNode child
)
483 if (child
== firstChild
)
484 firstChild
= child
.NextSibling
;
485 if (child
== lastChild
)
486 lastChild
= child
.PreviousSibling
;
487 child
.RemoveItself ();
491 public override string Value
{
493 StringBuilder sb
= new StringBuilder ();
495 return sb
.ToString ();
499 AppendChild (new XomText (value));
503 internal override void BuildValue (StringBuilder sb
)
505 for (XomNode n
= FirstChild
; n
!= null; n
= n
.NextSibling
)
510 public class XomRoot
: XomParentNode
512 XmlNameTable nameTable
;
513 Hashtable identicalElements
;
516 public XomRoot (XmlNameTable nameTable
)
518 this.nameTable
= nameTable
;
519 identicalElements
= new Hashtable ();
522 public override string BaseURI
{
523 get { return baseUri; }
526 public void Load (XmlReader reader
)
528 Load (reader
, XmlSpace
.None
);
531 public void Load (XmlReader reader
, XmlSpace space
)
533 baseUri
= reader
.BaseURI
;
535 ReadNode (reader
, space
);
538 public XmlNameTable NameTable
{
539 get { return nameTable; }
542 public override string Prefix
{
546 public override string LocalName
{
550 public override string Namespace
{
554 public override XPathNodeType NodeType
{
555 get { return XPathNodeType.Root; }
558 public override void Clear ()
563 public override void WriteTo (XmlWriter writer
)
565 for (XomNode n
= FirstChild
; n
!= null; n
= n
.NextSibling
)
569 public XomElement
GetIdenticalNode (string id
)
571 return identicalElements
[id
] as XomElement
;
574 internal void GetIdenticalNode (string id
, XomElement element
)
576 identicalElements
.Add (id
, element
);
580 public class XomElement
: XomParentNode
585 ArrayList attributes
;
586 ArrayList namespaces
;
588 public XomElement (string name
)
589 : this ("", name
, "", null)
593 public XomElement (string name
, XomRoot root
)
594 : this ("", name
, "", root
)
598 public XomElement (string name
, string ns
)
599 : this ("", name
, ns
, null)
603 public XomElement (string name
, string ns
, XomRoot root
)
604 : this ("", name
, ns
, root
)
608 public XomElement (string prefix
, string name
, string ns
)
609 : this (prefix
, name
, ns
, null)
613 public XomElement (string prefix
, string name
, string ns
, XomParentNode parent
)
615 qname
.LocalName
= name
;
616 qname
.Namespace
= ns
== null ? "" : ns
;
617 qname
.Prefix
= prefix
== null ? "" : prefix
;
619 parent
.AppendChild (this);
622 public override bool IsEmptyElement
{
623 get { return isEmptyElement; }
626 internal void SetIsEmpty (bool value)
628 isEmptyElement
= value;
631 public override string Prefix
{
632 get { return qname.Prefix; }
635 public override string LocalName
{
636 get { return qname.LocalName; }
639 public override string Namespace
{
640 get { return qname.Namespace; }
643 public override XPathNodeType NodeType
{
644 get { return XPathNodeType.Element; }
647 public override void Clear ()
649 if (attributes
!= null)
651 if (namespaces
!= null)
656 public void AppendAttribute (XomAttribute attr
)
658 if (attr
.Parent
!= null)
659 throw new InvalidOperationException ("The argument attribute already have another element owner.");
660 attr
.SetParent (this);
661 if (attributes
== null)
662 attributes
= new ArrayList ();
663 attributes
.Add (attr
);
667 public void UpdateAttribute (XomAttribute attr)
669 if (attr.Parent != null)
670 throw new InvalidOperationException ("The argument attribute already have another element owner.");
671 XomAttribute existing = GetAttribute (attr.LocalName, attr.Namespace);
672 if (existing != null)
673 RemoveAttribute (existing);
674 if (attributes == null)
675 attributes = new ArrayList ();
676 attr.SetParent (this);
677 attributes.Add (attr);
681 public XomAttribute
GetAttribute (int index
)
683 return attributes
== null ? null : attributes
[index
] as XomAttribute
;
686 public XomAttribute
GetAttribute (string name
, string ns
)
688 if (attributes
== null)
690 for (int i
= 0; i
< attributes
.Count
; i
++) {
691 XomAttribute a
= attributes
[i
] as XomAttribute
;
692 if (a
.LocalName
== name
&& a
.Namespace
== ns
)
698 public XomAttribute
GetNextAttribute (XomAttribute attr
)
700 if (attributes
== null || attributes
.Count
== 0)
702 if (attributes
[attributes
.Count
- 1] == attr
)
704 // It is not efficient, but usually there won't be so many attributes in an element.
705 int index
= attributes
.IndexOf (attr
);
708 return attributes
[index
+ 1] as XomAttribute
;
711 public int AttributeCount
{
712 get { return attributes == null ? 0 : attributes.Count; }
715 public void RemoveAttribute (XomAttribute attr
)
717 if (attributes
== null)
719 attributes
.Remove (attr
);
720 attr
.SetParent (null);
723 public void AppendNamespace (string prefix
, string ns
)
725 if (namespaces
== null)
726 namespaces
= new ArrayList ();
727 namespaces
.Add (new XomNamespace (prefix
, ns
));
730 public XomNamespace
GetLocalNamespace (int index
)
732 if (namespaces
== null || namespaces
.Count
<= index
)
734 return namespaces
[index
] as XomNamespace
;
737 public XomNamespace
GetLocalNamespace (string prefix
)
739 if (namespaces
== null)
741 for (int i
= 0; i
< namespaces
.Count
; i
++) {
742 XomNamespace qname
= namespaces
[i
] as XomNamespace
;
743 if (qname
.LocalName
== prefix
)
749 public XomNamespace
GetNextLocalNamespace (XomNamespace n
)
751 if (namespaces
== null || namespaces
.Count
== 0)
753 if (namespaces
[namespaces
.Count
- 1] == n
)
755 // It is not efficient, but usually there won't be so many attributes in an element.
756 int index
= namespaces
.IndexOf (n
);
759 return namespaces
[index
+ 1] as XomNamespace
;
762 public int NamespaceCount
{
763 get { return namespaces == null ? 0 : namespaces.Count; }
766 public void RemoveNamespace (string prefix
)
768 if (namespaces
== null)
770 for (int i
= 0; i
< namespaces
.Count
; i
++) {
771 XomNamespace qname
= namespaces
[i
] as XomNamespace
;
772 if (qname
.LocalName
== prefix
) {
773 namespaces
.RemoveAt (i
);
779 public override void WriteTo (XmlWriter writer
)
781 writer
.WriteStartElement (Prefix
, LocalName
, Namespace
);
782 if (namespaces
!= null) {
783 foreach (XomNamespace n
in namespaces
)
786 if (attributes
!= null) {
787 foreach (XomAttribute a
in attributes
)
791 for (XomNode n
= FirstChild
; n
!= null; n
= n
.NextSibling
)
794 writer
.WriteEndElement ();
798 public class XomAttribute
: XomNode
803 public XomAttribute (string name
, string value)
804 : this ("", name
, "", value, null)
808 public XomAttribute (string name
, string value, XomElement owner
)
809 : this ("", name
, "", value, owner
)
813 public XomAttribute (string name
, string ns
, string value)
814 : this ("", name
, ns
, value, null)
818 public XomAttribute (string name
, string ns
, string value, XomElement owner
)
819 : this ("", name
, ns
, value, owner
)
823 public XomAttribute (string prefix
, string name
, string ns
, string value)
824 : this ("", name
, ns
, value, null)
828 public XomAttribute (string prefix
, string name
, string ns
, string value, XomElement owner
)
830 qname
.LocalName
= name
;
831 qname
.Namespace
= ns
;
832 qname
.Prefix
= prefix
;
835 owner
.AppendAttribute (this);
838 public override string Prefix
{
839 get { return qname.Prefix; }
842 public override string LocalName
{
843 get { return qname.LocalName; }
846 public override string Namespace
{
847 get { return qname.Namespace; }
850 public override XPathNodeType NodeType
{
851 get { return XPathNodeType.Attribute; }
854 public override int ChildCount { get { return 0; }
}
856 public override string Value
{
857 get { return value; }
858 set { this.value = value; }
861 internal override void BuildValue (StringBuilder sb
)
866 public override void WriteTo (XmlWriter writer
)
868 writer
.WriteAttributeString (Prefix
, LocalName
, Namespace
, Value
);
872 public class XomNamespace
: XomNode
874 #region static members
875 static XomNamespace xml
;
877 static XomNamespace ()
879 xml
= new XomNamespace ("xml", "http://www.w3.org/XML/1998/namespace");
882 public static XomNamespace Xml
{
889 public XomNamespace (string prefix
, string ns
)
891 qname
.LocalName
= prefix
;
892 qname
.Namespace
= ns
;
895 public override int ChildCount
{
899 public override string LocalName
{
900 get { return qname.LocalName; }
903 public override string Prefix
{
904 get { return LocalName; }
907 public override string Namespace
{
908 get { return Value; }
911 public override string Value
{
912 get { return qname.Namespace; }
913 set { qname.Namespace = value; }
916 public override XPathNodeType NodeType
{
917 get { return XPathNodeType.Namespace; }
920 public override void WriteTo (XmlWriter writer
)
923 writer
.WriteAttributeString ("xmlns", LocalName
, "http://www.w3.org/2000/xmlns/", Namespace
);
925 writer
.WriteAttributeString ("", "xmlns", "http://www.w3.org/2000/xmlns/", Namespace
);
928 internal override void BuildValue (StringBuilder sb
)
934 public class XomComment
: XomNode
938 public XomComment (string value)
943 public XomComment (string value, XomParentNode parent
)
947 parent
.AppendChild (this);
950 public override string Prefix
{
954 public override string LocalName
{
958 public override string Namespace
{
962 public override XPathNodeType NodeType
{
963 get { return XPathNodeType.Comment; }
966 public override string Value
{
967 get { return value; }
968 set { this.value += value; }
971 internal override void BuildValue (StringBuilder sb
)
976 public override int ChildCount { get { return 0; }
}
978 public override void WriteTo (XmlWriter writer
)
980 writer
.WriteComment (Value
);
984 public class XomPI
: XomNode
989 public XomPI (string name
, string value)
990 : this (name
, value, null)
994 public XomPI (string name
, string value, XomParentNode parent
)
1001 parent
.AppendChild (this);
1004 public override string Prefix
{
1008 public override string LocalName
{
1009 get { return name; }
1012 public override string Namespace
{
1016 public override XPathNodeType NodeType
{
1017 get { return XPathNodeType.ProcessingInstruction; }
1020 public override string Value
{
1021 get { return value; }
1022 set { this.value += value; }
1025 internal override void BuildValue (StringBuilder sb
)
1030 public override int ChildCount { get { return 0; }
}
1032 public override void WriteTo (XmlWriter writer
)
1034 writer
.WriteProcessingInstruction (LocalName
, Value
);
1038 public class XomText
: XomNode
1042 public XomText (string value)
1043 : this (value, null)
1047 public XomText (string value, XomParentNode parent
)
1051 parent
.AppendChild (this);
1054 public override string Prefix
{
1058 public override string LocalName
{
1062 public override string Namespace
{
1066 public override string Value
{
1067 get { return value; }
1068 set { this.value += value; }
1071 public override XPathNodeType NodeType
{
1072 get { return XPathNodeType.Text; }
1075 internal override void BuildValue (StringBuilder sb
)
1080 public override int ChildCount { get { return 0; }
}
1082 public override void WriteTo (XmlWriter writer
)
1084 writer
.WriteString (Value
);
1088 public class XomWhitespace
: XomNode
1092 public XomWhitespace (string value)
1093 : this (value, null)
1097 public XomWhitespace (string value, XomParentNode parent
)
1101 parent
.AppendChild (this);
1104 public override string Prefix
{
1108 public override string LocalName
{
1112 public override string Namespace
{
1116 public override string Value
{
1117 get { return value; }
1118 set { this.value += value; }
1121 public override XPathNodeType NodeType
{
1122 get { return XPathNodeType.Whitespace; }
1125 internal override void BuildValue (StringBuilder sb
)
1130 public override int ChildCount { get { return 0; }
}
1132 public override void WriteTo (XmlWriter writer
)
1134 writer
.WriteString (Value
);
1138 public class XomSignificantWhitespace
: XomNode
1142 public XomSignificantWhitespace (string value)
1143 : this (value, null)
1147 public XomSignificantWhitespace (string value, XomParentNode parent
)
1151 parent
.AppendChild (this);
1154 public override string Prefix
{
1158 public override string LocalName
{
1162 public override string Namespace
{
1166 public override string Value
{
1167 get { return value; }
1168 set { this.value += value; }
1171 public override XPathNodeType NodeType
{
1172 get { return XPathNodeType.SignificantWhitespace; }
1175 internal override void BuildValue (StringBuilder sb
)
1180 public override int ChildCount { get { return 0; }
}
1182 public override void WriteTo (XmlWriter writer
)
1184 writer
.WriteString (Value
);