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, 400)
26 public DTMXPathDocumentBuilder (string url, XmlSpace space)
27 : this (url, space, 400)
31 public DTMXPathDocumentBuilder (string url, XmlSpace space, int defaultCapacity)
32 : this (new XmlTextReader (url), space, defaultCapacity)
36 public DTMXPathDocumentBuilder (XmlReader reader)
37 : this (reader, XmlSpace.None, 400)
41 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space)
42 : this (reader, space, 400)
46 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space, int defaultCapacity)
48 this.xmlReader = reader;
49 this.validatingReader = reader as XmlValidatingReader;
50 lineInfo = reader as IXmlLineInfo;
51 this.xmlSpace = space;
52 this.nameTable = reader.NameTable;
53 nodeCapacity = nodeCapacity;
54 attributeCapacity = nodeCapacity * 2;
59 XmlValidatingReader validatingReader;
61 XmlNameTable nameTable;
62 IXmlLineInfo lineInfo;
63 int nodeCapacity = 400;
64 int attributeCapacity = 800;
68 DTMXPathLinkedNode [] nodes = new DTMXPathLinkedNode [0];
71 DTMXPathAttributeNode [] attributes = new DTMXPathAttributeNode [0];
74 DTMXPathNamespaceNode [] namespaces = new DTMXPathNamespaceNode [0];
76 // idTable [string value] -> int nodeId
82 bool requireFirstChildFill;
86 bool skipRead = false;
88 public DTMXPathDocument CreateDocument ()
90 return new DTMXPathDocument (nameTable,
98 public void Compile ()
100 idTable = new Hashtable ();
102 // index 0 is dummy. No node (including Root) is assigned to this index
103 // So that we can easily compare index != 0 instead of index < 0.
104 // (Difference between jnz or jbe in 80x86.)
105 AddNode (0, 0, 0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
107 AddAttribute (0, null, null, null, null, null, 0, 0);
108 // attributes [0].NextAttribute = 0;
109 AddNsNode (0, null, null);
111 // nextNsNode_ [0] = 0;
112 AddNsNode (1, "xml", XmlNamespaces.XML);
113 // nextNsNode_ [1] = 0;
116 AddNode (0, 0, 0, 0, -1, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
119 this.requireFirstChildFill = true;
121 while (!xmlReader.EOF)
123 SetNodeArrayLength (nodeIndex + 1);
124 SetAttributeArrayLength (attributeIndex + 1);
125 SetNsArrayLength (nsIndex + 1);
127 xmlReader = null; // It is no more required.
133 if (!xmlReader.Read ())
136 int parent = nodeIndex;
138 if (nodes [nodeIndex].Depth >= xmlReader.Depth) { // not ">=" ? But == worked when with ArrayList...
139 // if not, then current node is parent.
140 while (xmlReader.Depth <= nodes [parent].Depth)
141 parent = nodes [parent].Parent;
144 prevSibling = nodeIndex;
146 switch (xmlReader.NodeType) {
147 case XmlNodeType.Element:
148 case XmlNodeType.CDATA:
149 case XmlNodeType.SignificantWhitespace:
150 case XmlNodeType.Comment:
151 case XmlNodeType.Text:
152 case XmlNodeType.ProcessingInstruction:
153 if (requireFirstChildFill)
156 while (nodes [prevSibling].Depth != xmlReader.Depth)
157 prevSibling = nodes [prevSibling].Parent;
158 if (prevSibling != 0)
159 position = nodes [prevSibling].Position + 1;
163 if (prevSibling != 0)
164 nodes [prevSibling].NextSibling = nodeIndex;
165 if (requireFirstChildFill)
166 nodes [parent].FirstChild = nodeIndex;
168 case XmlNodeType.Whitespace:
169 if (xmlSpace == XmlSpace.Preserve)
170 goto case XmlNodeType.Text;
173 case XmlNodeType.EndElement:
174 requireFirstChildFill = false;
177 // No operations. Doctype, EntityReference,
181 requireFirstChildFill = false; // Might be changed in ProcessElement().
184 XPathNodeType nodeType = xmlReader.NodeType == XmlNodeType.Whitespace ?
185 XPathNodeType.Whitespace : XPathNodeType.Text;
187 switch (xmlReader.NodeType) {
188 case XmlNodeType.Element:
189 ProcessElement (parent, prevSibling, position);
191 case XmlNodeType.CDATA:
192 case XmlNodeType.SignificantWhitespace:
193 case XmlNodeType.Text:
194 case XmlNodeType.Whitespace:
205 xmlReader.IsEmptyElement,
206 xmlReader.LocalName, // for PI
207 xmlReader.NamespaceURI, // for PI
212 lineInfo != null ? lineInfo.LineNumber : 0,
213 lineInfo != null ? lineInfo.LinePosition : 0);
214 // this code is tricky, but after ReadString() invokation,
215 // xmlReader is moved to next node!!
217 nodes [nodeIndex].Value = xmlReader.ReadString ();
219 case XmlNodeType.Comment:
220 value = xmlReader.Value;
221 nodeType = XPathNodeType.Comment;
222 goto case XmlNodeType.Text;
223 case XmlNodeType.ProcessingInstruction:
224 value = xmlReader.Value;
225 nodeType = XPathNodeType.ProcessingInstruction;
226 goto case XmlNodeType.Text;
230 private void ProcessElement (int parent, int previousSibling, int position)
232 int firstAttributeIndex = 0;
233 int lastNsIndexInCurrent = 0;
236 // process namespaces and attributes.
237 if (xmlReader.MoveToFirstAttribute ()) {
239 if (xmlReader.NamespaceURI == XmlNamespaces.XMLNS) {
240 // add namespace node.
243 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [parent].FirstNamespace : lastNsIndexInCurrent;
245 this.AddNsNode (nodeIndex,
246 (xmlReader.Prefix == null || xmlReader.Prefix == String.Empty) ?
247 "" : xmlReader.LocalName,
249 namespaces [nsIndex].NextNamespace = nextTmp;
250 // if (lastNsIndexInCurrent == 0)
251 // nodes [nodeIndex].FirstNamespace = nsIndex;
252 lastNsIndexInCurrent = nsIndex;
254 // add attribute node.
256 this.AddAttribute (nodeIndex,
258 xmlReader.NamespaceURI,
259 xmlReader.Prefix != null ? xmlReader.Prefix : String.Empty,
262 lineInfo != null ? lineInfo.LineNumber : 0,
263 lineInfo != null ? lineInfo.LinePosition : 0);
264 if (firstAttributeIndex == 0)
265 firstAttributeIndex = attributeIndex;
267 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
268 // dummy for "current" attribute.
269 attributes [attributeIndex].NextAttribute = 0;
272 if (validatingReader != null) {
273 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
275 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
277 dt = xsType.Datatype;
279 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
280 idTable.Add (xmlReader.Value, nodeIndex);
283 } while (xmlReader.MoveToNextAttribute ());
284 xmlReader.MoveToElement ();
293 XPathNodeType.Element,
295 xmlReader.IsEmptyElement,
297 xmlReader.NamespaceURI,
299 "", // Element has no internal value.
302 lineInfo != null ? lineInfo.LineNumber : 0,
303 lineInfo != null ? lineInfo.LinePosition : 0);
304 if (!xmlReader.IsEmptyElement)
305 requireFirstChildFill = true;
308 private void SetNodeArrayLength (int size)
310 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
311 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
315 private void SetAttributeArrayLength (int size)
317 DTMXPathAttributeNode [] newArr =
318 new DTMXPathAttributeNode [size];
319 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
323 private void SetNsArrayLength (int size)
325 DTMXPathNamespaceNode [] newArr =
326 new DTMXPathNamespaceNode [size];
327 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
331 // Here followings are skipped: firstChild, nextSibling,
332 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)
334 if (nodes.Length < nodeIndex + 1) {
335 // if (nodes.Length >= nodeCapacity) {
337 SetNodeArrayLength (nodeCapacity);
341 DTMXPathLinkedNode curNode = nodes [nodeIndex];// = new DTMXPathLinkedNode ();
342 nodes [nodeIndex].FirstChild = 0; // dummy
343 nodes [nodeIndex].Parent = parent;
344 nodes [nodeIndex].FirstAttribute = firstAttribute;
345 nodes [nodeIndex].PreviousSibling = previousSibling;
346 nodes [nodeIndex].NextSibling = 0; // dummy
347 nodes [nodeIndex].Depth = depth;
348 nodes [nodeIndex].Position = position;
349 nodes [nodeIndex].NodeType = nodeType;
350 nodes [nodeIndex].BaseURI = baseUri;
351 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
352 nodes [nodeIndex].LocalName = localName;
353 nodes [nodeIndex].NamespaceURI = ns;
354 nodes [nodeIndex].Prefix = prefix;
355 nodes [nodeIndex].Value = value;
356 nodes [nodeIndex].XmlLang = xmlLang;
357 nodes [nodeIndex].FirstNamespace = namespaceNode;
358 nodes [nodeIndex].LineNumber = lineNumber;
359 nodes [nodeIndex].LinePosition = linePosition;
362 // Followings are skipped: nextAttribute,
363 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
365 if (attributes.Length < attributeIndex + 1) {
366 // if (attributes.Length >= attributeCapacity) {
367 attributeCapacity *= 2;
368 SetAttributeArrayLength (attributeCapacity);
372 DTMXPathAttributeNode attr = attributes [attributeIndex];// = new DTMXPathAttributeNode ();
373 attributes [attributeIndex].OwnerElement = ownerElement;
374 attributes [attributeIndex].LocalName = localName;
375 attributes [attributeIndex].NamespaceURI = ns;
376 attributes [attributeIndex].Prefix = prefix;
377 attributes [attributeIndex].Value = value;
378 attributes [attributeIndex].SchemaType = schemaType;
379 attributes [attributeIndex].LineNumber = lineNumber;
380 attributes [attributeIndex].LinePosition = linePosition;
383 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
384 public void AddNsNode (int declaredElement, string name, string ns)
386 if (namespaces.Length < nsIndex + 1) {
387 // if (namespaces.Length >= nsCapacity) {
389 SetNsArrayLength (nsCapacity);
393 DTMXPathNamespaceNode nsNode = namespaces [nsIndex];// = new DTMXPathNamespaceNode ();
394 namespaces [nsIndex].DeclaredElement = declaredElement;
395 namespaces [nsIndex].Name = name;
396 namespaces [nsIndex].Namespace = ns;