2 // Mono.Xml.XPath.DTMXPathDocumentBuilder2
5 // Atsushi Enomoto <atsushi@ximian.com>
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
34 using System.Xml.Schema;
35 using System.Xml.XPath;
37 namespace Mono.Xml.XPath
40 #if OUTSIDE_SYSTEM_XML
45 class DTMXPathDocumentBuilder2
47 public DTMXPathDocumentBuilder2 (string url)
48 : this (url, XmlSpace.None, 200)
52 public DTMXPathDocumentBuilder2 (string url, XmlSpace space)
53 : this (url, space, 200)
57 public DTMXPathDocumentBuilder2 (string url, XmlSpace space, int defaultCapacity)
61 r = new XmlTextReader (url);
62 Init (r, space, defaultCapacity);
69 public DTMXPathDocumentBuilder2 (XmlReader reader)
70 : this (reader, XmlSpace.None, 200)
74 public DTMXPathDocumentBuilder2 (XmlReader reader, XmlSpace space)
75 : this (reader, space, 200)
79 public DTMXPathDocumentBuilder2 (XmlReader reader, XmlSpace space, int defaultCapacity)
81 Init (reader, space, defaultCapacity);
84 private void Init (XmlReader reader, XmlSpace space, int defaultCapacity)
86 this.xmlReader = reader;
87 this.validatingReader = reader as XmlValidatingReader;
88 lineInfo = reader as IXmlLineInfo;
89 this.xmlSpace = space;
90 this.nameTable = reader.NameTable;
91 nodeCapacity = defaultCapacity;
92 attributeCapacity = nodeCapacity;
94 idTable = new Hashtable ();
96 nodes = new DTMXPathLinkedNode2 [nodeCapacity];
97 attributes = new DTMXPathAttributeNode2 [attributeCapacity];
98 namespaces = new DTMXPathNamespaceNode2 [nsCapacity];
99 atomicStringPool = new string [20];
100 nonAtomicStringPool = new string [20];
106 XmlValidatingReader validatingReader;
108 XmlNameTable nameTable;
109 IXmlLineInfo lineInfo;
111 int attributeCapacity;
115 DTMXPathLinkedNode2 [] nodes;
118 DTMXPathAttributeNode2 [] attributes;
121 DTMXPathNamespaceNode2 [] namespaces;
124 string [] atomicStringPool;
126 string [] nonAtomicStringPool;
129 // idTable [string value] -> int nodeId
136 // for attribute processing; should be reset per each element.
139 int attrIndexAtStart;
143 bool skipRead = false;
145 int [] parentStack = new int [10];
146 int parentStackIndex = 0;
148 public DTMXPathDocument2 CreateDocument ()
150 return new DTMXPathDocument2 (nameTable,
160 public void Compile ()
162 // string pool index 0 to 3 are fixed.
163 atomicStringPool [0] = nonAtomicStringPool [0] = "";
164 atomicStringPool [1] = nonAtomicStringPool [1] = null;
165 atomicStringPool [2] = nonAtomicStringPool [2] = XmlNamespaces.XML;
166 atomicStringPool [3] = nonAtomicStringPool [3] = XmlNamespaces.XMLNS;
167 atomicIndex = nonAtomicIndex = 4;
169 // index 0 is dummy. No node (including Root) is assigned to this index
170 // So that we can easily compare index != 0 instead of index < 0.
171 // (Difference between jnz or jbe in 80x86.)
172 AddNode (0, 0, 0, XPathNodeType.All, 0, false, 0, 0, 0, 0, 0, 0, 0, 0);
174 AddAttribute (0, 0, 0, 0, 0, 0, 0);
175 AddNsNode (0, 0, 0, 0);
177 AddNsNode (1, AtomicIndex ("xml"), AtomicIndex (XmlNamespaces.XML), 0);
180 AddNode (0, 0, 0, XPathNodeType.Root, AtomicIndex (xmlReader.BaseURI), false, 0, 0, 0, 0, 0, 1, 0, 0);
183 this.lastNsInScope = 1;
184 parentStack [0] = nodeIndex;
186 while (!xmlReader.EOF)
188 SetNodeArrayLength (nodeIndex + 1);
189 SetAttributeArrayLength (attributeIndex + 1);
190 SetNsArrayLength (nsIndex + 1);
192 string [] newArr = new string [atomicIndex];
193 Array.Copy (atomicStringPool, newArr, atomicIndex);
194 atomicStringPool = newArr;
196 newArr = new string [nonAtomicIndex];
197 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
198 nonAtomicStringPool = newArr;
200 xmlReader = null; // It is no more required.
206 if (!xmlReader.Read ())
209 int parent = parentStack [parentStackIndex];
210 int prevSibling = nodeIndex;
212 switch (xmlReader.NodeType) {
213 case XmlNodeType.Element:
214 case XmlNodeType.CDATA:
215 case XmlNodeType.SignificantWhitespace:
216 case XmlNodeType.Comment:
217 case XmlNodeType.Text:
218 case XmlNodeType.ProcessingInstruction:
219 if (parent == nodeIndex)
222 while (nodes [prevSibling].Parent != parent)
223 prevSibling = nodes [prevSibling].Parent;
227 if (prevSibling != 0)
228 nodes [prevSibling].NextSibling = nodeIndex;
229 if (parentStack [parentStackIndex] == nodeIndex - 1)
230 nodes [parent].FirstChild = nodeIndex;
232 case XmlNodeType.Whitespace:
233 if (xmlSpace == XmlSpace.Preserve)
234 goto case XmlNodeType.Text;
237 case XmlNodeType.EndElement:
241 // No operations. Doctype, EntityReference,
246 XPathNodeType nodeType = XPathNodeType.Text;
248 switch (xmlReader.NodeType) {
249 case XmlNodeType.Element:
250 ProcessElement (parent, prevSibling);
252 case XmlNodeType.SignificantWhitespace:
253 nodeType = XPathNodeType.SignificantWhitespace;
254 goto case XmlNodeType.Text;
255 case XmlNodeType.Whitespace:
256 nodeType = XPathNodeType.Whitespace;
257 goto case XmlNodeType.Text;
258 case XmlNodeType.CDATA:
259 case XmlNodeType.Text:
264 AtomicIndex (xmlReader.BaseURI),
265 xmlReader.IsEmptyElement,
266 AtomicIndex (xmlReader.LocalName), // for PI
267 AtomicIndex (xmlReader.NamespaceURI), // for PI
268 AtomicIndex (xmlReader.Prefix),
269 value == null ? 0 : NonAtomicIndex (value),
270 AtomicIndex (xmlReader.XmlLang),
272 lineInfo != null ? lineInfo.LineNumber : 0,
273 lineInfo != null ? lineInfo.LinePosition : 0);
274 // this code is tricky, but after sequential
275 // Read() invokation, xmlReader is moved to
279 value = String.Empty;
280 XPathNodeType type = XPathNodeType.Whitespace;
282 switch (xmlReader.NodeType) {
283 case XmlNodeType.Text:
284 case XmlNodeType.CDATA:
285 type = XPathNodeType.Text;
286 goto case XmlNodeType.Whitespace;
287 case XmlNodeType.SignificantWhitespace:
288 if (type == XPathNodeType.Whitespace)
289 type = XPathNodeType.SignificantWhitespace;
290 goto case XmlNodeType.Whitespace;
291 case XmlNodeType.Whitespace:
292 if (xmlReader.NodeType != XmlNodeType.Whitespace || xmlSpace == XmlSpace.Preserve)
293 value += xmlReader.Value;
294 loop = xmlReader.Read ();
302 nodes [nodeIndex].Value = NonAtomicIndex (value);
303 nodes [nodeIndex].NodeType = type;
306 case XmlNodeType.Comment:
307 value = xmlReader.Value;
308 nodeType = XPathNodeType.Comment;
309 goto case XmlNodeType.Text;
310 case XmlNodeType.ProcessingInstruction:
311 value = xmlReader.Value;
312 nodeType = XPathNodeType.ProcessingInstruction;
313 goto case XmlNodeType.Text;
317 private void ProcessElement (int parent, int previousSibling)
319 WriteStartElement (parent, previousSibling);
321 // process namespaces and attributes.
322 if (xmlReader.MoveToFirstAttribute ()) {
324 string prefix = xmlReader.Prefix;
325 string ns = xmlReader.NamespaceURI;
326 if (ns == XmlNamespaces.XMLNS)
327 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : xmlReader.LocalName, xmlReader.Value);
329 ProcessAttribute (prefix, xmlReader.LocalName, ns, xmlReader.Value);
331 } while (xmlReader.MoveToNextAttribute ());
332 xmlReader.MoveToElement ();
335 CloseStartElement ();
338 private void PrepareStartElement (int previousSibling)
340 hasAttributes = false;
342 attrIndexAtStart = attributeIndex;
343 nsIndexAtStart = nsIndex;
345 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
346 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
350 private void WriteStartElement (int parent, int previousSibling)
352 PrepareStartElement (previousSibling);
355 0, // dummy:firstAttribute
357 XPathNodeType.Element,
358 AtomicIndex (xmlReader.BaseURI),
359 xmlReader.IsEmptyElement,
360 AtomicIndex (xmlReader.LocalName),
361 AtomicIndex (xmlReader.NamespaceURI),
362 AtomicIndex (xmlReader.Prefix),
363 0, // Element has no internal value.
364 AtomicIndex (xmlReader.XmlLang),
366 lineInfo != null ? lineInfo.LineNumber : 0,
367 lineInfo != null ? lineInfo.LinePosition : 0);
371 private void CloseStartElement ()
373 if (attrIndexAtStart != attributeIndex)
374 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
375 if (nsIndexAtStart != nsIndex) {
376 nodes [nodeIndex].FirstNamespace = nsIndex;
377 if (!xmlReader.IsEmptyElement)
378 lastNsInScope = nsIndex;
381 if (!nodes [nodeIndex].IsEmptyElement) {
383 if (parentStack.Length == parentStackIndex) {
384 int [] tmp = new int [parentStackIndex * 2];
385 Array.Copy (parentStack, tmp, parentStackIndex);
388 parentStack [parentStackIndex] = nodeIndex;
392 private void ProcessNamespace (string prefix, string ns)
394 int nextTmp = hasLocalNs ?
395 nsIndex : nodes [nodeIndex].FirstNamespace;
399 this.AddNsNode (nodeIndex,
400 AtomicIndex (prefix),
406 private void ProcessAttribute (string prefix, string localName, string ns, string value)
410 this.AddAttribute (nodeIndex,
411 AtomicIndex (localName),
413 prefix != null ? AtomicIndex (prefix) : 0,
414 NonAtomicIndex (value),
415 lineInfo != null ? lineInfo.LineNumber : 0,
416 lineInfo != null ? lineInfo.LinePosition : 0);
418 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
420 hasAttributes = true;
423 if (validatingReader != null) {
424 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
426 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
428 dt = xsType.Datatype;
430 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
431 idTable.Add (value, nodeIndex);
435 private int AtomicIndex (string s)
442 for (; i < atomicIndex; i++)
443 if (Object.ReferenceEquals (s, atomicStringPool [i]))
446 if (atomicIndex == atomicStringPool.Length) {
447 string [] newArr = new string [atomicIndex * 2];
448 Array.Copy (atomicStringPool, newArr, atomicIndex);
449 atomicStringPool = newArr;
451 atomicStringPool [atomicIndex] = s;
452 return atomicIndex++;
455 private int NonAtomicIndex (string s)
463 // Here we don't compare all the entries (sometimes it
464 // goes extremely slow).
465 int max = nonAtomicIndex < 100 ? nonAtomicIndex : 100;
467 if (s == nonAtomicStringPool [i])
470 if (nonAtomicIndex == nonAtomicStringPool.Length) {
471 string [] newArr = new string [nonAtomicIndex * 2];
472 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
473 nonAtomicStringPool = newArr;
475 nonAtomicStringPool [nonAtomicIndex] = s;
476 return nonAtomicIndex++;
479 private void SetNodeArrayLength (int size)
481 DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
482 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
486 private void SetAttributeArrayLength (int size)
488 DTMXPathAttributeNode2 [] newArr =
489 new DTMXPathAttributeNode2 [size];
490 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
494 private void SetNsArrayLength (int size)
496 DTMXPathNamespaceNode2 [] newArr =
497 new DTMXPathNamespaceNode2 [size];
498 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
502 // Here followings are skipped: firstChild, nextSibling,
503 public void AddNode (int parent, int firstAttribute, int previousSibling, XPathNodeType nodeType, int baseUri, bool isEmptyElement, int localName, int ns, int prefix, int value, int xmlLang, int namespaceNode, int lineNumber, int linePosition)
505 if (nodes.Length < nodeIndex + 1) {
507 SetNodeArrayLength (nodeCapacity);
511 nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
513 nodes [nodeIndex].FirstChild = 0; // dummy
514 nodes [nodeIndex].Parent = parent;
515 nodes [nodeIndex].FirstAttribute = firstAttribute;
516 nodes [nodeIndex].PreviousSibling = previousSibling;
517 nodes [nodeIndex].NextSibling = 0; // dummy
518 nodes [nodeIndex].NodeType = nodeType;
519 nodes [nodeIndex].BaseURI = baseUri;
520 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
521 nodes [nodeIndex].LocalName = localName;
522 nodes [nodeIndex].NamespaceURI = ns;
523 nodes [nodeIndex].Prefix = prefix;
524 nodes [nodeIndex].Value = value;
525 nodes [nodeIndex].XmlLang = xmlLang;
526 nodes [nodeIndex].FirstNamespace = namespaceNode;
527 nodes [nodeIndex].LineNumber = lineNumber;
528 nodes [nodeIndex].LinePosition = linePosition;
531 // Followings are skipped: nextAttribute,
532 public void AddAttribute (int ownerElement, int localName, int ns, int prefix, int value, int lineNumber, int linePosition)
534 if (attributes.Length < attributeIndex + 1) {
535 attributeCapacity *= 4;
536 SetAttributeArrayLength (attributeCapacity);
540 attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
542 attributes [attributeIndex].OwnerElement = ownerElement;
543 attributes [attributeIndex].LocalName = localName;
544 attributes [attributeIndex].NamespaceURI = ns;
545 attributes [attributeIndex].Prefix = prefix;
546 attributes [attributeIndex].Value = value;
547 attributes [attributeIndex].LineNumber = lineNumber;
548 attributes [attributeIndex].LinePosition = linePosition;
551 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
552 public void AddNsNode (int declaredElement, int name, int ns, int nextNs)
554 if (namespaces.Length < nsIndex + 1) {
556 SetNsArrayLength (nsCapacity);
560 namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
562 namespaces [nsIndex].DeclaredElement = declaredElement;
563 namespaces [nsIndex].Name = name;
564 namespaces [nsIndex].Namespace = ns;
565 namespaces [nsIndex].NextNamespace = nextNs;