2 // Mono.Xml.XPath.DTMXPathDocumentBuilder
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C) 2003 Atsushi Enomoto
10 using System.Collections;
13 using System.Xml.Schema;
14 using System.Xml.XPath;
16 namespace Mono.Xml.XPath
19 public class DTMXPathDocumentBuilder
21 public DTMXPathDocumentBuilder (string url)
22 : this (url, XmlSpace.None, false, 100)
26 public DTMXPathDocumentBuilder (string url, XmlSpace space)
27 : this (url, space, false, 100)
31 public DTMXPathDocumentBuilder (string url, XmlSpace space, bool supportID, int defaultCapacity)
32 : this (new XmlTextReader (url), space, supportID, defaultCapacity)
36 public DTMXPathDocumentBuilder (XmlReader reader)
37 : this (reader, XmlSpace.None, false, 100)
41 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space)
42 : this (reader, space, false, 100)
46 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space, bool supportID, int defaultCapacity)
48 this.xmlReader = reader;
50 this.validatingReader = reader as XmlValidatingReader;
51 lineInfo = reader as IXmlLineInfo;
52 this.xmlSpace = xmlSpace;
53 this.nameTable = reader.NameTable;
54 DefaultCapacity = defaultCapacity;
60 XmlValidatingReader validatingReader;
62 XmlNameTable nameTable;
63 IXmlLineInfo lineInfo;
64 int defaultCapacity = 100;
65 public int DefaultCapacity {
66 get { return defaultCapacity; }
69 throw new ArgumentOutOfRangeException ();
70 defaultCapacity = value;
74 #region Tree node info collections.
76 int [] firstChild_ = new int [0];
77 int [] parent_ = new int [0];
78 int [] firstAttribute_ = new int [0];
79 int [] previousSibling_ = new int [0];
80 int [] nextSibling_ = new int [0];
81 int [] depth_ = new int [0];
82 int [] position_ = new int [0];
83 XPathNodeType [] nodeType_ = new XPathNodeType [0];
84 string [] baseUri_ = new string [0];
85 bool [] isEmptyElement_ = new bool [0];
86 string [] localName_ = new string [0];
87 string [] namespaceUri_ = new string [0];
88 string [] prefix_ = new string [0];
89 string [] value_ = new string [0];
90 string [] xmlLang_ = new string [0];
91 int [] namespaceNode_ = new int [0];
92 int [] nodeLineNumber_ = new int [0];
93 int [] nodeLinePosition_ = new int [0];
96 int [] ownerElement_ = new int [0];
97 int [] nextAttribute_ = new int [0];
98 string [] attrLocalName_ = new string [0];
99 string [] attrPrefix_ = new string [0];
100 string [] attrNsUri_ = new string [0];
101 string [] attrValue_ = new string [0];
102 object [] attrSchemaType_ = new object [0];
103 int [] attrLineNumber_ = new int [0];
104 int [] attrLinePosition_ = new int [0];
107 int [] nsDeclaredElement_ = new int [100];
108 int [] nextNsNode_ = new int [100];
109 string [] nsNodeName_ = new string [100];
110 string [] nsNodeUri_ = new string [100];
112 // idTable [string value] -> int nodeId
119 bool requireFirstChildFill;
121 public DTMXPathDocument CreateDocument ()
123 return new DTMXPathDocument (nameTable,
163 public void Compile ()
165 idTable_ = new Hashtable ();
167 // index 0 is dummy. No node (including Root) is assigned to this index
168 // So that we can easily compare index != 0 instead of index < 0.
169 // (Difference between jnz or jbe in 80x86.)
170 AddNode (0, 0, 0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
172 AddAttribute (0, null, null, null, null, null, 0, 0);
173 nextAttribute_ [0] = 0;
174 AddNsNode (0, null, null);
177 AddNsNode (1, "xml", XmlNamespaces.XML);
181 AddNode (0, 0, 0, 0, -1, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
184 this.requireFirstChildFill = true;
186 while (!xmlReader.EOF)
188 SetNodeArraysLength (nodeIndex + 1);
189 SetAttributeArraysLength (attributeIndex + 1);
190 SetNsArraysLength (nsIndex + 1);
192 xmlReader = null; // It is no more required.
197 bool skipRead = false;
202 if (!xmlReader.Read ())
205 int parent = nodeIndex;
207 if (depth_ [nodeIndex] >= xmlReader.Depth) { // not ">=" ? But == worked when with ArrayList...
208 // if not, then current node is parent.
209 while (xmlReader.Depth <= depth_ [parent])
210 parent = parent_ [parent];
213 prevSibling = nodeIndex;
215 switch (xmlReader.NodeType) {
216 case XmlNodeType.Element:
217 case XmlNodeType.CDATA:
218 case XmlNodeType.SignificantWhitespace:
219 case XmlNodeType.Comment:
220 case XmlNodeType.Text:
221 case XmlNodeType.ProcessingInstruction:
222 if (requireFirstChildFill)
225 while (depth_ [prevSibling] != xmlReader.Depth)
226 prevSibling = parent_ [prevSibling];
227 if (prevSibling != 0)
228 position = position_ [prevSibling] + 1;
232 if (prevSibling != 0)
233 nextSibling_ [prevSibling] = nodeIndex;
234 if (requireFirstChildFill)
235 firstChild_ [parent] = nodeIndex;
237 case XmlNodeType.Whitespace:
238 if (xmlSpace == XmlSpace.Preserve)
239 goto case XmlNodeType.Text;
242 case XmlNodeType.EndElement:
243 requireFirstChildFill = false;
246 // No operations. Doctype, EntityReference,
250 requireFirstChildFill = false; // Might be changed in ProcessElement().
253 XPathNodeType nodeType = XPathNodeType.Text;
255 switch (xmlReader.NodeType) {
256 case XmlNodeType.Element:
257 ProcessElement (parent, prevSibling, position);
259 case XmlNodeType.CDATA:
260 case XmlNodeType.SignificantWhitespace:
261 case XmlNodeType.Text:
272 xmlReader.IsEmptyElement,
273 xmlReader.LocalName, // for PI
274 xmlReader.NamespaceURI, // for PI
279 lineInfo != null ? lineInfo.LineNumber : 0,
280 lineInfo != null ? lineInfo.LinePosition : 0);
281 // this code is tricky, but after ReadString() invokation,
282 // xmlReader is moved to next node!!
284 value_ [nodeIndex] = xmlReader.ReadString ();
286 case XmlNodeType.Comment:
287 value = xmlReader.Value;
288 nodeType = XPathNodeType.Comment;
289 goto case XmlNodeType.Text;
290 case XmlNodeType.Whitespace:
291 goto case XmlNodeType.Text;
292 case XmlNodeType.ProcessingInstruction:
293 value = xmlReader.Value;
294 nodeType = XPathNodeType.ProcessingInstruction;
295 goto case XmlNodeType.Text;
299 private void ProcessElement (int parent, int previousSibling, int position)
301 int firstAttributeIndex = 0;
302 int lastNsIndexInCurrent = 0;
305 // process namespaces and attributes.
306 if (xmlReader.MoveToFirstAttribute ()) {
308 if (xmlReader.NamespaceURI == XmlNamespaces.XMLNS) {
309 // add namespace node.
312 nextNsNode_ [nsIndex] = lastNsIndexInCurrent == 0 ? namespaceNode_ [parent] : lastNsIndexInCurrent;
314 if (lastNsIndexInCurrent == 0)
315 namespaceNode_ [nodeIndex] = nsIndex;
316 this.AddNsNode (nodeIndex,
317 (xmlReader.Prefix == null || xmlReader.Prefix == String.Empty) ?
318 "" : xmlReader.LocalName,
320 lastNsIndexInCurrent = nsIndex;
322 // add attribute node.
324 this.AddAttribute (nodeIndex,
326 xmlReader.NamespaceURI,
327 xmlReader.Prefix != null ? xmlReader.Prefix : String.Empty,
330 lineInfo != null ? lineInfo.LineNumber : 0,
331 lineInfo != null ? lineInfo.LinePosition : 0);
332 if (firstAttributeIndex == 0)
333 firstAttributeIndex = attributeIndex;
335 nextAttribute_ [attributeIndex - 1] = attributeIndex;
336 // dummy for "current" attribute.
337 nextAttribute_ [attributeIndex] = 0;
340 if (validatingReader != null) {
341 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
343 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
344 dt = xsType.Datatype;
346 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
347 idTable_.Add (xmlReader.Value, nodeIndex);
350 } while (xmlReader.MoveToNextAttribute ());
351 xmlReader.MoveToElement ();
360 XPathNodeType.Element,
362 xmlReader.IsEmptyElement,
364 xmlReader.NamespaceURI,
366 "", // Element has no internal value.
369 lineInfo != null ? lineInfo.LineNumber : 0,
370 lineInfo != null ? lineInfo.LinePosition : 0);
371 if (!xmlReader.IsEmptyElement)
372 requireFirstChildFill = true;
375 private void SetObjectArrayLength (ref object [] a, int length)
377 object [] arr = new object [length];
378 Array.Copy (a, arr, System.Math.Min (a.Length, length));
382 private void SetBoolArrayLength (ref bool [] a, int length)
384 bool [] bArr = new bool [length];
385 Array.Copy (a, bArr, System.Math.Min (a.Length, length));
389 private void SetXPathNodeTypeArrayLength (ref XPathNodeType [] a, int length)
391 XPathNodeType [] arr = new XPathNodeType [length];
392 Array.Copy (a, arr, System.Math.Min (a.Length, length));
396 private void SetIntArrayLength (ref int [] a, int length)
398 int [] intArr = new int [length];
399 Array.Copy (a, intArr, System.Math.Min (a.Length, length));
403 private void SetStringArrayLength (ref string [] a, int length)
405 string [] strArr = new string [length];
406 Array.Copy (a, strArr, System.Math.Min (a.Length, length));
410 private void SetNodeArraysLength (int size)
412 SetIntArrayLength (ref firstChild_, size);
413 SetIntArrayLength (ref parent_, size);
414 SetIntArrayLength (ref firstAttribute_, size);
415 SetIntArrayLength (ref previousSibling_, size);
416 SetIntArrayLength (ref nextSibling_, size);
417 SetIntArrayLength (ref depth_, size);
418 SetIntArrayLength (ref position_, size);
419 SetXPathNodeTypeArrayLength (ref nodeType_, size);
420 SetStringArrayLength (ref baseUri_, size);
421 SetBoolArrayLength (ref isEmptyElement_, size);
422 SetStringArrayLength (ref localName_, size);
423 SetStringArrayLength (ref namespaceUri_, size);
424 SetStringArrayLength (ref prefix_, size);
425 SetStringArrayLength (ref value_, size);
426 SetStringArrayLength (ref xmlLang_, size);
427 SetIntArrayLength (ref namespaceNode_, size);
428 SetIntArrayLength (ref nodeLineNumber_, size);
429 SetIntArrayLength (ref nodeLinePosition_, size);
432 private void SetAttributeArraysLength (int size)
434 SetIntArrayLength (ref ownerElement_, size);
435 SetIntArrayLength (ref nextAttribute_, size);
436 SetStringArrayLength (ref attrLocalName_, size);
437 SetStringArrayLength (ref attrPrefix_, size);
438 SetStringArrayLength (ref attrNsUri_, size);
439 SetStringArrayLength (ref attrValue_, size);
440 SetObjectArrayLength (ref attrSchemaType_, size);
441 SetIntArrayLength (ref attrLineNumber_, size);
442 SetIntArrayLength (ref attrLinePosition_, size);
445 private void SetNsArraysLength (int size)
447 SetIntArrayLength (ref nsDeclaredElement_, size);
448 SetIntArrayLength (ref nextNsNode_, size);
449 SetStringArrayLength (ref nsNodeName_, size);
450 SetStringArrayLength (ref nsNodeUri_, size);
453 // Here followings are skipped: firstChild, nextSibling,
454 public void AddNode (int parent, int firstAttribute, int attributeEnd, int previousSibling, int depth, int position, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
456 if (firstChild_.Length < nodeIndex + 1) {
457 if (firstChild_.Length >= defaultCapacity)
458 defaultCapacity *= 2;
459 SetNodeArraysLength (defaultCapacity);
462 firstChild_ [nodeIndex] = 0; // dummy
463 parent_ [nodeIndex] = parent;
464 firstAttribute_ [nodeIndex] = firstAttribute;
465 previousSibling_ [nodeIndex] = previousSibling;
466 nextSibling_ [nodeIndex] = 0; // dummy
467 depth_ [nodeIndex] = depth;
468 position_ [nodeIndex] = position;
469 nodeType_ [nodeIndex] = nodeType;
470 baseUri_ [nodeIndex] = baseUri;
471 isEmptyElement_ [nodeIndex] = isEmptyElement;
472 localName_ [nodeIndex] = localName;
473 namespaceUri_ [nodeIndex] = ns;
474 prefix_ [nodeIndex] = prefix;
475 value_ [nodeIndex] = value;
476 xmlLang_ [nodeIndex] = xmlLang;
477 namespaceNode_ [nodeIndex] = namespaceNode;
478 nodeLineNumber_ [nodeIndex] = lineNumber;
479 nodeLinePosition_ [nodeIndex] = linePosition;
482 // Followings are skipped: nextAttribute,
483 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
485 if (ownerElement_.Length < attributeIndex + 1) {
486 if (ownerElement_.Length >= defaultCapacity)
487 defaultCapacity *= 2;
488 SetAttributeArraysLength (defaultCapacity);
491 ownerElement_ [attributeIndex] = ownerElement;
492 attrLocalName_ [attributeIndex] = localName;
493 attrNsUri_ [attributeIndex] = ns;
494 attrPrefix_ [attributeIndex] = prefix;
495 attrValue_ [attributeIndex] = value;
496 attrSchemaType_ [attributeIndex] = schemaType;
497 attrLineNumber_ [attributeIndex] = lineNumber;
498 attrLinePosition_ [attributeIndex] = linePosition;
501 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
502 public void AddNsNode (int declaredElement, string name, string ns)
504 if (nsDeclaredElement_.Length < nsIndex + 1) {
505 if (nsDeclaredElement_.Length >= defaultCapacity)
506 defaultCapacity *= 2;
507 SetNsArraysLength (defaultCapacity);
510 nsDeclaredElement_ [nsIndex] = declaredElement;
511 nsNodeName_ [nsIndex] = name;
512 nsNodeUri_ [nsIndex] = ns;