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)
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 if (xmlReader.ReadState == ReadState.Initial)
188 int startDepth = xmlReader.Depth;
191 } while (skipRead || xmlReader.Read () && xmlReader.Depth >= startDepth);
192 SetNodeArrayLength (nodeIndex + 1);
193 SetAttributeArrayLength (attributeIndex + 1);
194 SetNsArrayLength (nsIndex + 1);
196 string [] newArr = new string [atomicIndex];
197 Array.Copy (atomicStringPool, newArr, atomicIndex);
198 atomicStringPool = newArr;
200 newArr = new string [nonAtomicIndex];
201 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
202 nonAtomicStringPool = newArr;
204 xmlReader = null; // It is no more required.
211 int parent = parentStack [parentStackIndex];
212 int prevSibling = nodeIndex;
214 switch (xmlReader.NodeType) {
215 case XmlNodeType.Element:
216 case XmlNodeType.CDATA:
217 case XmlNodeType.Whitespace:
218 case XmlNodeType.SignificantWhitespace:
219 case XmlNodeType.Comment:
220 case XmlNodeType.Text:
221 case XmlNodeType.ProcessingInstruction:
223 case XmlNodeType.EndElement:
224 int endedNode = parentStack [parentStackIndex];
225 AdjustLastNsInScope (endedNode);
229 // No operations. Doctype, EntityReference,
234 XPathNodeType nodeType = XPathNodeType.Root; // dummy
235 bool containsCDATA = false;
237 switch (xmlReader.NodeType) {
238 case XmlNodeType.Element:
239 nodeType = XPathNodeType.Element;
240 goto case XmlNodeType.None;
241 case XmlNodeType.SignificantWhitespace:
242 case XmlNodeType.Whitespace:
243 case XmlNodeType.CDATA:
244 case XmlNodeType.Text:
247 switch (xmlReader.NodeType) {
248 case XmlNodeType.Whitespace:
250 case XPathNodeType.Root:
251 nodeType = XPathNodeType.Whitespace;
254 // whitespaces after CDATA are ignored
256 value += xmlReader.Value;
258 case XmlNodeType.SignificantWhitespace:
259 if (nodeType == XPathNodeType.Root ||
260 nodeType == XPathNodeType.Whitespace)
261 nodeType = XPathNodeType.SignificantWhitespace;
262 value += xmlReader.Value;
264 case XmlNodeType.CDATA:
265 // whitespaces before CDATA are ignored
266 if (nodeType != XPathNodeType.Text)
267 value = String.Empty;
268 containsCDATA = true;
269 goto case XmlNodeType.Text;
270 case XmlNodeType.Text:
271 nodeType = XPathNodeType.Text;
272 value += xmlReader.Value;
276 } while (xmlReader.Read ());
277 goto case XmlNodeType.None;
278 case XmlNodeType.None:
279 if (nodeType == XPathNodeType.Root ||
280 nodeType == XPathNodeType.Whitespace && xmlSpace != XmlSpace.Preserve)
281 return; // do not process as a node.
283 // prepare a slot for new node.
284 if (parent == nodeIndex)
287 while (nodes [prevSibling].Parent != parent)
288 prevSibling = nodes [prevSibling].Parent;
292 if (prevSibling != 0)
293 nodes [prevSibling].NextSibling = nodeIndex;
294 if (parentStack [parentStackIndex] == nodeIndex - 1)
295 nodes [parent].FirstChild = nodeIndex;
298 if (nodeType == XPathNodeType.Element) {
299 ProcessElement (parent, prevSibling);
306 AtomicIndex (xmlReader.BaseURI),
307 xmlReader.IsEmptyElement,
308 skipRead ? 0 : AtomicIndex (xmlReader.LocalName), // for PI
309 skipRead ? 0 : AtomicIndex (xmlReader.NamespaceURI), // for PI
310 AtomicIndex (xmlReader.Prefix),
311 value == null ? 0 : NonAtomicIndex (value),
312 AtomicIndex (xmlReader.XmlLang),
314 lineInfo != null ? lineInfo.LineNumber : 0,
315 lineInfo != null ? lineInfo.LinePosition : 0);
317 case XmlNodeType.Comment:
318 value = xmlReader.Value;
319 nodeType = XPathNodeType.Comment;
320 goto case XmlNodeType.None;
321 case XmlNodeType.ProcessingInstruction:
322 value = xmlReader.Value;
323 nodeType = XPathNodeType.ProcessingInstruction;
324 goto case XmlNodeType.None;
328 private void ProcessElement (int parent, int previousSibling)
330 WriteStartElement (parent, previousSibling);
332 // process namespaces and attributes.
333 if (xmlReader.MoveToFirstAttribute ()) {
335 string prefix = xmlReader.Prefix;
336 string ns = xmlReader.NamespaceURI;
337 if (ns == XmlNamespaces.XMLNS)
338 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : xmlReader.LocalName, xmlReader.Value);
340 ProcessAttribute (prefix, xmlReader.LocalName, ns, xmlReader.Value);
342 } while (xmlReader.MoveToNextAttribute ());
343 xmlReader.MoveToElement ();
346 CloseStartElement ();
349 private void PrepareStartElement (int previousSibling)
351 hasAttributes = false;
353 attrIndexAtStart = attributeIndex;
354 nsIndexAtStart = nsIndex;
355 AdjustLastNsInScope (previousSibling);
358 private void AdjustLastNsInScope (int target)
360 while (namespaces [lastNsInScope].DeclaredElement == target) {
361 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
365 private void WriteStartElement (int parent, int previousSibling)
367 PrepareStartElement (previousSibling);
370 0, // dummy:firstAttribute
372 XPathNodeType.Element,
373 AtomicIndex (xmlReader.BaseURI),
374 xmlReader.IsEmptyElement,
375 AtomicIndex (xmlReader.LocalName),
376 AtomicIndex (xmlReader.NamespaceURI),
377 AtomicIndex (xmlReader.Prefix),
378 0, // Element has no internal value.
379 AtomicIndex (xmlReader.XmlLang),
381 lineInfo != null ? lineInfo.LineNumber : 0,
382 lineInfo != null ? lineInfo.LinePosition : 0);
386 private void CloseStartElement ()
388 if (attrIndexAtStart != attributeIndex)
389 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
390 if (nsIndexAtStart != nsIndex) {
391 nodes [nodeIndex].FirstNamespace = nsIndex;
392 if (!xmlReader.IsEmptyElement)
393 lastNsInScope = nsIndex;
396 if (!nodes [nodeIndex].IsEmptyElement) {
398 if (parentStack.Length == parentStackIndex) {
399 int [] tmp = new int [parentStackIndex * 2];
400 Array.Copy (parentStack, tmp, parentStackIndex);
403 parentStack [parentStackIndex] = nodeIndex;
407 private void ProcessNamespace (string prefix, string ns)
409 int nextTmp = hasLocalNs ?
410 nsIndex : nodes [nodeIndex].FirstNamespace;
414 this.AddNsNode (nodeIndex,
415 AtomicIndex (prefix),
421 private void ProcessAttribute (string prefix, string localName, string ns, string value)
425 this.AddAttribute (nodeIndex,
426 AtomicIndex (localName),
428 prefix != null ? AtomicIndex (prefix) : 0,
429 NonAtomicIndex (value),
430 lineInfo != null ? lineInfo.LineNumber : 0,
431 lineInfo != null ? lineInfo.LinePosition : 0);
433 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
435 hasAttributes = true;
438 if (validatingReader != null) {
439 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
441 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
443 dt = xsType.Datatype;
445 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
446 idTable.Add (value, nodeIndex);
450 private int AtomicIndex (string s)
457 for (; i < atomicIndex; i++)
458 if (Object.ReferenceEquals (s, atomicStringPool [i]))
461 if (atomicIndex == atomicStringPool.Length) {
462 string [] newArr = new string [atomicIndex * 2];
463 Array.Copy (atomicStringPool, newArr, atomicIndex);
464 atomicStringPool = newArr;
466 atomicStringPool [atomicIndex] = s;
467 return atomicIndex++;
470 private int NonAtomicIndex (string s)
478 // Here we don't compare all the entries (sometimes it
479 // goes extremely slow).
480 int max = nonAtomicIndex < 100 ? nonAtomicIndex : 100;
482 if (s == nonAtomicStringPool [i])
485 if (nonAtomicIndex == nonAtomicStringPool.Length) {
486 string [] newArr = new string [nonAtomicIndex * 2];
487 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
488 nonAtomicStringPool = newArr;
490 nonAtomicStringPool [nonAtomicIndex] = s;
491 return nonAtomicIndex++;
494 private void SetNodeArrayLength (int size)
496 DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
497 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
501 private void SetAttributeArrayLength (int size)
503 DTMXPathAttributeNode2 [] newArr =
504 new DTMXPathAttributeNode2 [size];
505 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
509 private void SetNsArrayLength (int size)
511 DTMXPathNamespaceNode2 [] newArr =
512 new DTMXPathNamespaceNode2 [size];
513 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
517 // Here followings are skipped: firstChild, nextSibling,
518 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)
520 if (nodes.Length < nodeIndex + 1) {
522 SetNodeArrayLength (nodeCapacity);
526 nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
528 nodes [nodeIndex].FirstChild = 0; // dummy
529 nodes [nodeIndex].Parent = parent;
530 nodes [nodeIndex].FirstAttribute = firstAttribute;
531 nodes [nodeIndex].PreviousSibling = previousSibling;
532 nodes [nodeIndex].NextSibling = 0; // dummy
533 nodes [nodeIndex].NodeType = nodeType;
534 nodes [nodeIndex].BaseURI = baseUri;
535 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
536 nodes [nodeIndex].LocalName = localName;
537 nodes [nodeIndex].NamespaceURI = ns;
538 nodes [nodeIndex].Prefix = prefix;
539 nodes [nodeIndex].Value = value;
540 nodes [nodeIndex].XmlLang = xmlLang;
541 nodes [nodeIndex].FirstNamespace = namespaceNode;
542 nodes [nodeIndex].LineNumber = lineNumber;
543 nodes [nodeIndex].LinePosition = linePosition;
546 // Followings are skipped: nextAttribute,
547 public void AddAttribute (int ownerElement, int localName, int ns, int prefix, int value, int lineNumber, int linePosition)
549 if (attributes.Length < attributeIndex + 1) {
550 attributeCapacity *= 4;
551 SetAttributeArrayLength (attributeCapacity);
555 attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
557 attributes [attributeIndex].OwnerElement = ownerElement;
558 attributes [attributeIndex].LocalName = localName;
559 attributes [attributeIndex].NamespaceURI = ns;
560 attributes [attributeIndex].Prefix = prefix;
561 attributes [attributeIndex].Value = value;
562 attributes [attributeIndex].LineNumber = lineNumber;
563 attributes [attributeIndex].LinePosition = linePosition;
566 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
567 public void AddNsNode (int declaredElement, int name, int ns, int nextNs)
569 if (namespaces.Length < nsIndex + 1) {
571 SetNsArrayLength (nsCapacity);
575 namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
577 namespaces [nsIndex].DeclaredElement = declaredElement;
578 namespaces [nsIndex].Name = name;
579 namespaces [nsIndex].Namespace = ns;
580 namespaces [nsIndex].NextNamespace = nextNs;
581 if (lineInfo != null && lineInfo.HasLineInfo ()) {
582 namespaces [nsIndex].LineNumber = lineInfo.LineNumber;
583 namespaces [nsIndex].LinePosition = lineInfo.LinePosition;