2 // Mono.Xml.XPath.DTMXPathDocumentBuilder
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C) 2003 Atsushi Enomoto
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Collections
;
35 using System
.Xml
.Schema
;
36 using System
.Xml
.XPath
;
38 namespace Mono
.Xml
.XPath
41 #if OUTSIDE_SYSTEM_XML
46 class DTMXPathDocumentBuilder
48 public DTMXPathDocumentBuilder (string url
)
49 : this (url
, XmlSpace
.None
, 200)
53 public DTMXPathDocumentBuilder (string url
, XmlSpace space
)
54 : this (url
, space
, 200)
58 public DTMXPathDocumentBuilder (string url
, XmlSpace space
, int defaultCapacity
)
62 r
= new XmlTextReader (url
);
63 Init (r
, space
, defaultCapacity
);
70 public DTMXPathDocumentBuilder (XmlReader reader
)
71 : this (reader
, XmlSpace
.None
, 200)
75 public DTMXPathDocumentBuilder (XmlReader reader
, XmlSpace space
)
76 : this (reader
, space
, 200)
80 public DTMXPathDocumentBuilder (XmlReader reader
, XmlSpace space
, int defaultCapacity
)
82 Init (reader
, space
, defaultCapacity
);
85 private void Init (XmlReader reader
, XmlSpace space
, int defaultCapacity
)
87 this.xmlReader
= reader
;
88 this.validatingReader
= reader
as XmlValidatingReader
;
89 lineInfo
= reader
as IXmlLineInfo
;
90 this.xmlSpace
= space
;
91 this.nameTable
= reader
.NameTable
;
92 nodeCapacity
= defaultCapacity
;
93 attributeCapacity
= nodeCapacity
;
94 idTable
= new Hashtable ();
96 nodes
= new DTMXPathLinkedNode
[nodeCapacity
];
97 attributes
= new DTMXPathAttributeNode
[attributeCapacity
];
98 namespaces
= new DTMXPathNamespaceNode
[0];
104 XmlValidatingReader validatingReader
;
106 XmlNameTable nameTable
;
107 IXmlLineInfo lineInfo
;
108 int nodeCapacity
= 200;
109 int attributeCapacity
= 200;
113 DTMXPathLinkedNode
[] nodes
;
116 DTMXPathAttributeNode
[] attributes
;
119 DTMXPathNamespaceNode
[] namespaces
;
121 // idTable [string value] -> int nodeId
127 int parentForFirstChild
;
129 // for attribute processing; should be reset per each element.
130 int firstAttributeIndex
;
131 int lastNsIndexInCurrent
;
132 int attrIndexAtStart
;
137 bool skipRead
= false;
139 public DTMXPathDocument
CreateDocument ()
141 return new DTMXPathDocument (nameTable
,
149 public void Compile ()
151 // index 0 is dummy. No node (including Root) is assigned to this index
152 // So that we can easily compare index != 0 instead of index < 0.
153 // (Difference between jnz or jbe in 80x86.)
154 AddNode (0, 0, 0, 0, XPathNodeType
.All
, "", false, "", "", "", "", "", 0, 0, 0);
156 AddAttribute (0, null, null, null, null, null, 0, 0);
157 AddNsNode (0, null, null, 0);
159 AddNsNode (1, "xml", XmlNamespaces
.XML
, 0);
162 AddNode (0, 0, 0, -1, XPathNodeType
.Root
, xmlReader
.BaseURI
, false, "", "", "", "", "", 1, 0, 0);
165 this.lastNsInScope
= 1;
166 this.parentForFirstChild
= nodeIndex
;
168 while (!xmlReader
.EOF
)
170 SetNodeArrayLength (nodeIndex
+ 1);
171 SetAttributeArrayLength (attributeIndex
+ 1);
172 SetNsArrayLength (nsIndex
+ 1);
174 xmlReader
= null; // It is no more required.
180 if (!xmlReader
.Read ())
183 int parent
= nodeIndex
;
185 if (nodes
[nodeIndex
].Depth
>= xmlReader
.Depth
) {
186 // if not, then current node is parent.
187 while (xmlReader
.Depth
<= nodes
[parent
].Depth
)
188 parent
= nodes
[parent
].Parent
;
191 prevSibling
= nodeIndex
;
192 switch (xmlReader
.NodeType
) {
193 case XmlNodeType
.Element
:
194 case XmlNodeType
.CDATA
:
195 case XmlNodeType
.SignificantWhitespace
:
196 case XmlNodeType
.Comment
:
197 case XmlNodeType
.Text
:
198 case XmlNodeType
.ProcessingInstruction
:
199 if (parentForFirstChild
>= 0)
202 while (nodes
[prevSibling
].Depth
!= xmlReader
.Depth
)
203 prevSibling
= nodes
[prevSibling
].Parent
;
207 if (prevSibling
!= 0)
208 nodes
[prevSibling
].NextSibling
= nodeIndex
;
209 if (parentForFirstChild
>= 0)
210 nodes
[parent
].FirstChild
= nodeIndex
;
212 case XmlNodeType
.Whitespace
:
213 if (xmlSpace
== XmlSpace
.Preserve
)
214 goto case XmlNodeType
.Text
;
217 case XmlNodeType
.EndElement
:
218 parentForFirstChild
= -1;
221 // No operations. Doctype, EntityReference,
225 parentForFirstChild
= -1; // Might be changed in ProcessElement().
228 XPathNodeType nodeType
= xmlReader
.NodeType
== XmlNodeType
.Whitespace
?
229 XPathNodeType
.Whitespace
: XPathNodeType
.Text
;
231 switch (xmlReader
.NodeType
) {
232 case XmlNodeType
.Element
:
233 ProcessElement (parent
, prevSibling
);
235 case XmlNodeType
.CDATA
:
236 case XmlNodeType
.SignificantWhitespace
:
237 case XmlNodeType
.Text
:
238 case XmlNodeType
.Whitespace
:
247 xmlReader
.IsEmptyElement
,
248 xmlReader
.LocalName
, // for PI
249 xmlReader
.NamespaceURI
, // for PI
254 lineInfo
!= null ? lineInfo
.LineNumber
: 0,
255 lineInfo
!= null ? lineInfo
.LinePosition
: 0);
256 // this code is tricky, but after ReadString() invokation,
257 // xmlReader is moved to next node!!
259 nodes
[nodeIndex
].Value
= xmlReader
.ReadString ();
261 case XmlNodeType
.Comment
:
262 value = xmlReader
.Value
;
263 nodeType
= XPathNodeType
.Comment
;
264 goto case XmlNodeType
.Text
;
265 case XmlNodeType
.ProcessingInstruction
:
266 value = xmlReader
.Value
;
267 nodeType
= XPathNodeType
.ProcessingInstruction
;
268 goto case XmlNodeType
.Text
;
272 private void ProcessElement (int parent
, int previousSibling
)
274 WriteStartElement (parent
, previousSibling
);
276 // process namespaces and attributes.
277 if (xmlReader
.MoveToFirstAttribute ()) {
279 string prefix
= xmlReader
.Prefix
;
280 string ns
= xmlReader
.NamespaceURI
;
281 if (ns
== XmlNamespaces
.XMLNS
)
282 ProcessNamespace ((prefix
== null || prefix
== String
.Empty
) ? "" : xmlReader
.LocalName
, xmlReader
.Value
);
284 ProcessAttribute (prefix
, xmlReader
.LocalName
, ns
, xmlReader
.Value
);
286 } while (xmlReader
.MoveToNextAttribute ());
287 xmlReader
.MoveToElement ();
290 CloseStartElement ();
293 private void PrepareStartElement (int previousSibling
)
295 firstAttributeIndex
= 0;
296 lastNsIndexInCurrent
= 0;
297 attrIndexAtStart
= attributeIndex
;
298 nsIndexAtStart
= nsIndex
;
300 while (namespaces
[lastNsInScope
].DeclaredElement
== previousSibling
) {
301 lastNsInScope
= namespaces
[lastNsInScope
].NextNamespace
;
305 private void WriteStartElement (int parent
, int previousSibling
)
307 PrepareStartElement (previousSibling
);
310 0, // dummy:firstAttribute
313 XPathNodeType
.Element
,
315 xmlReader
.IsEmptyElement
,
317 xmlReader
.NamespaceURI
,
319 "", // Element has no internal value.
322 lineInfo
!= null ? lineInfo
.LineNumber
: 0,
323 lineInfo
!= null ? lineInfo
.LinePosition
: 0);
327 private void CloseStartElement ()
329 if (attrIndexAtStart
!= attributeIndex
)
330 nodes
[nodeIndex
].FirstAttribute
= attrIndexAtStart
+ 1;
331 if (nsIndexAtStart
!= nsIndex
) {
332 nodes
[nodeIndex
].FirstNamespace
= nsIndex
;
333 lastNsInScope
= nsIndex
;
336 if (!nodes
[nodeIndex
].IsEmptyElement
)
337 parentForFirstChild
= nodeIndex
;
340 private void ProcessNamespace (string prefix
, string ns
)
344 int nextTmp
= lastNsIndexInCurrent
== 0 ? nodes
[nodeIndex
].FirstNamespace
: lastNsIndexInCurrent
;
346 this.AddNsNode (nodeIndex
,
350 lastNsIndexInCurrent
= nsIndex
;
353 private void ProcessAttribute (string prefix
, string localName
, string ns
, string value)
357 this.AddAttribute (nodeIndex
,
360 prefix
!= null ? prefix
: String
.Empty
,
363 lineInfo
!= null ? lineInfo
.LineNumber
: 0,
364 lineInfo
!= null ? lineInfo
.LinePosition
: 0);
365 if (firstAttributeIndex
== 0)
366 firstAttributeIndex
= attributeIndex
;
368 attributes
[attributeIndex
- 1].NextAttribute
= attributeIndex
;
371 if (validatingReader
!= null) {
372 XmlSchemaDatatype dt
= validatingReader
.SchemaType
as XmlSchemaDatatype
;
374 XmlSchemaType xsType
= validatingReader
.SchemaType
as XmlSchemaType
;
376 dt
= xsType
.Datatype
;
378 if (dt
!= null && dt
.TokenizedType
== XmlTokenizedType
.ID
)
379 idTable
.Add (value, nodeIndex
);
383 private void SetNodeArrayLength (int size
)
385 DTMXPathLinkedNode
[] newArr
= new DTMXPathLinkedNode
[size
];
386 Array
.Copy (nodes
, newArr
, System
.Math
.Min (size
, nodes
.Length
));
390 private void SetAttributeArrayLength (int size
)
392 DTMXPathAttributeNode
[] newArr
=
393 new DTMXPathAttributeNode
[size
];
394 Array
.Copy (attributes
, newArr
, System
.Math
.Min (size
, attributes
.Length
));
398 private void SetNsArrayLength (int size
)
400 DTMXPathNamespaceNode
[] newArr
=
401 new DTMXPathNamespaceNode
[size
];
402 Array
.Copy (namespaces
, newArr
, System
.Math
.Min (size
, namespaces
.Length
));
406 // Here followings are skipped: firstChild, nextSibling,
407 public void AddNode (int parent
, int firstAttribute
, int previousSibling
, int depth
, XPathNodeType nodeType
, string baseUri
, bool isEmptyElement
, string localName
, string ns
, string prefix
, string value, string xmlLang
, int namespaceNode
, int lineNumber
, int linePosition
)
409 if (nodes
.Length
< nodeIndex
+ 1) {
411 SetNodeArrayLength (nodeCapacity
);
415 nodes
[nodeIndex
] = new DTMXPathLinkedNode ();
417 nodes
[nodeIndex
].FirstChild
= 0; // dummy
418 nodes
[nodeIndex
].Parent
= parent
;
419 nodes
[nodeIndex
].FirstAttribute
= firstAttribute
;
420 nodes
[nodeIndex
].PreviousSibling
= previousSibling
;
421 nodes
[nodeIndex
].NextSibling
= 0; // dummy
422 nodes
[nodeIndex
].Depth
= depth
;
423 nodes
[nodeIndex
].NodeType
= nodeType
;
424 nodes
[nodeIndex
].BaseURI
= baseUri
;
425 nodes
[nodeIndex
].IsEmptyElement
= isEmptyElement
;
426 nodes
[nodeIndex
].LocalName
= localName
;
427 nodes
[nodeIndex
].NamespaceURI
= ns
;
428 nodes
[nodeIndex
].Prefix
= prefix
;
429 nodes
[nodeIndex
].Value
= value;
430 nodes
[nodeIndex
].XmlLang
= xmlLang
;
431 nodes
[nodeIndex
].FirstNamespace
= namespaceNode
;
432 nodes
[nodeIndex
].LineNumber
= lineNumber
;
433 nodes
[nodeIndex
].LinePosition
= linePosition
;
436 // Followings are skipped: nextAttribute,
437 public void AddAttribute (int ownerElement
, string localName
, string ns
, string prefix
, string value, object schemaType
, int lineNumber
, int linePosition
)
439 if (attributes
.Length
< attributeIndex
+ 1) {
440 attributeCapacity
*= 4;
441 SetAttributeArrayLength (attributeCapacity
);
445 attributes
[attributeIndex
] = new DTMXPathAttributeNode ();
447 attributes
[attributeIndex
].OwnerElement
= ownerElement
;
448 attributes
[attributeIndex
].LocalName
= localName
;
449 attributes
[attributeIndex
].NamespaceURI
= ns
;
450 attributes
[attributeIndex
].Prefix
= prefix
;
451 attributes
[attributeIndex
].Value
= value;
452 attributes
[attributeIndex
].SchemaType
= schemaType
;
453 attributes
[attributeIndex
].LineNumber
= lineNumber
;
454 attributes
[attributeIndex
].LinePosition
= linePosition
;
457 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
458 public void AddNsNode (int declaredElement
, string name
, string ns
, int nextNs
)
460 if (namespaces
.Length
< nsIndex
+ 1) {
462 SetNsArrayLength (nsCapacity
);
466 namespaces
[nsIndex
] = new DTMXPathNamespaceNode ();
468 namespaces
[nsIndex
].DeclaredElement
= declaredElement
;
469 namespaces
[nsIndex
].Name
= name
;
470 namespaces
[nsIndex
].Namespace
= ns
;
471 namespaces
[nsIndex
].NextNamespace
= nextNs
;