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;
87 bool skipRead = false;
89 public DTMXPathDocument CreateDocument ()
91 return new DTMXPathDocument (nameTable,
99 public void Compile ()
101 idTable = new Hashtable ();
103 // index 0 is dummy. No node (including Root) is assigned to this index
104 // So that we can easily compare index != 0 instead of index < 0.
105 // (Difference between jnz or jbe in 80x86.)
106 AddNode (0, 0, 0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
108 AddAttribute (0, null, null, null, null, null, 0, 0);
109 // attributes [0].NextAttribute = 0;
110 AddNsNode (0, null, null);
112 // nextNsNode_ [0] = 0;
113 AddNsNode (1, "xml", XmlNamespaces.XML);
114 // nextNsNode_ [1] = 0;
117 AddNode (0, 0, 0, 0, -1, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
120 this.lastNsInScope = 1;
121 this.requireFirstChildFill = true;
123 while (!xmlReader.EOF)
125 SetNodeArrayLength (nodeIndex + 1);
126 SetAttributeArrayLength (attributeIndex + 1);
127 SetNsArrayLength (nsIndex + 1);
129 xmlReader = null; // It is no more required.
135 if (!xmlReader.Read ())
138 int parent = nodeIndex;
140 if (nodes [nodeIndex].Depth >= xmlReader.Depth) { // not ">=" ? But == worked when with ArrayList...
141 // if not, then current node is parent.
142 while (xmlReader.Depth <= nodes [parent].Depth)
143 parent = nodes [parent].Parent;
146 prevSibling = nodeIndex;
148 switch (xmlReader.NodeType) {
149 case XmlNodeType.Element:
150 case XmlNodeType.CDATA:
151 case XmlNodeType.SignificantWhitespace:
152 case XmlNodeType.Comment:
153 case XmlNodeType.Text:
154 case XmlNodeType.ProcessingInstruction:
155 if (requireFirstChildFill)
158 while (nodes [prevSibling].Depth != xmlReader.Depth)
159 prevSibling = nodes [prevSibling].Parent;
160 if (prevSibling != 0)
161 position = nodes [prevSibling].Position + 1;
165 if (prevSibling != 0)
166 nodes [prevSibling].NextSibling = nodeIndex;
167 if (requireFirstChildFill)
168 nodes [parent].FirstChild = nodeIndex;
170 case XmlNodeType.Whitespace:
171 if (xmlSpace == XmlSpace.Preserve)
172 goto case XmlNodeType.Text;
175 case XmlNodeType.EndElement:
176 requireFirstChildFill = false;
179 // No operations. Doctype, EntityReference,
183 requireFirstChildFill = false; // Might be changed in ProcessElement().
186 XPathNodeType nodeType = xmlReader.NodeType == XmlNodeType.Whitespace ?
187 XPathNodeType.Whitespace : XPathNodeType.Text;
189 switch (xmlReader.NodeType) {
190 case XmlNodeType.Element:
191 ProcessElement (parent, prevSibling, position);
193 case XmlNodeType.CDATA:
194 case XmlNodeType.SignificantWhitespace:
195 case XmlNodeType.Text:
196 case XmlNodeType.Whitespace:
207 xmlReader.IsEmptyElement,
208 xmlReader.LocalName, // for PI
209 xmlReader.NamespaceURI, // for PI
214 lineInfo != null ? lineInfo.LineNumber : 0,
215 lineInfo != null ? lineInfo.LinePosition : 0);
216 // this code is tricky, but after ReadString() invokation,
217 // xmlReader is moved to next node!!
219 nodes [nodeIndex].Value = xmlReader.ReadString ();
221 case XmlNodeType.Comment:
222 value = xmlReader.Value;
223 nodeType = XPathNodeType.Comment;
224 goto case XmlNodeType.Text;
225 case XmlNodeType.ProcessingInstruction:
226 value = xmlReader.Value;
227 nodeType = XPathNodeType.ProcessingInstruction;
228 goto case XmlNodeType.Text;
232 private void ProcessElement (int parent, int previousSibling, int position)
234 int firstAttributeIndex = 0;
235 int lastNsIndexInCurrent = 0;
237 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
238 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
241 // process namespaces and attributes.
242 if (xmlReader.MoveToFirstAttribute ()) {
244 if (xmlReader.NamespaceURI == XmlNamespaces.XMLNS) {
245 // add namespace node.
248 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [parent].FirstNamespace : lastNsIndexInCurrent;
250 this.AddNsNode (nodeIndex,
251 (xmlReader.Prefix == null || xmlReader.Prefix == String.Empty) ?
252 "" : xmlReader.LocalName,
254 namespaces [nsIndex].NextNamespace = nextTmp;
255 lastNsIndexInCurrent = nsIndex;
257 // add attribute node.
259 this.AddAttribute (nodeIndex,
261 xmlReader.NamespaceURI,
262 xmlReader.Prefix != null ? xmlReader.Prefix : String.Empty,
265 lineInfo != null ? lineInfo.LineNumber : 0,
266 lineInfo != null ? lineInfo.LinePosition : 0);
267 if (firstAttributeIndex == 0)
268 firstAttributeIndex = attributeIndex;
270 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
271 // dummy for "current" attribute.
272 attributes [attributeIndex].NextAttribute = 0;
275 if (validatingReader != null) {
276 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
278 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
280 dt = xsType.Datatype;
282 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
283 idTable.Add (xmlReader.Value, nodeIndex);
286 } while (xmlReader.MoveToNextAttribute ());
287 xmlReader.MoveToElement ();
290 if (lastNsIndexInCurrent > 0)
291 lastNsInScope = nsIndex;
299 XPathNodeType.Element,
301 xmlReader.IsEmptyElement,
303 xmlReader.NamespaceURI,
305 "", // Element has no internal value.
308 lineInfo != null ? lineInfo.LineNumber : 0,
309 lineInfo != null ? lineInfo.LinePosition : 0);
310 if (!xmlReader.IsEmptyElement)
311 requireFirstChildFill = true;
314 private void SetNodeArrayLength (int size)
316 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
317 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
321 private void SetAttributeArrayLength (int size)
323 DTMXPathAttributeNode [] newArr =
324 new DTMXPathAttributeNode [size];
325 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
329 private void SetNsArrayLength (int size)
331 DTMXPathNamespaceNode [] newArr =
332 new DTMXPathNamespaceNode [size];
333 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
337 // Here followings are skipped: firstChild, nextSibling,
338 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)
340 if (nodes.Length < nodeIndex + 1) {
342 SetNodeArrayLength (nodeCapacity);
345 DTMXPathLinkedNode curNode = nodes [nodeIndex];
346 nodes [nodeIndex].FirstChild = 0; // dummy
347 nodes [nodeIndex].Parent = parent;
348 nodes [nodeIndex].FirstAttribute = firstAttribute;
349 nodes [nodeIndex].PreviousSibling = previousSibling;
350 nodes [nodeIndex].NextSibling = 0; // dummy
351 nodes [nodeIndex].Depth = depth;
352 nodes [nodeIndex].Position = position;
353 nodes [nodeIndex].NodeType = nodeType;
354 nodes [nodeIndex].BaseURI = baseUri;
355 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
356 nodes [nodeIndex].LocalName = localName;
357 nodes [nodeIndex].NamespaceURI = ns;
358 nodes [nodeIndex].Prefix = prefix;
359 nodes [nodeIndex].Value = value;
360 nodes [nodeIndex].XmlLang = xmlLang;
361 nodes [nodeIndex].FirstNamespace = namespaceNode;
362 nodes [nodeIndex].LineNumber = lineNumber;
363 nodes [nodeIndex].LinePosition = linePosition;
366 // Followings are skipped: nextAttribute,
367 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
369 if (attributes.Length < attributeIndex + 1) {
370 attributeCapacity *= 2;
371 SetAttributeArrayLength (attributeCapacity);
374 DTMXPathAttributeNode attr = attributes [attributeIndex];
375 attributes [attributeIndex].OwnerElement = ownerElement;
376 attributes [attributeIndex].LocalName = localName;
377 attributes [attributeIndex].NamespaceURI = ns;
378 attributes [attributeIndex].Prefix = prefix;
379 attributes [attributeIndex].Value = value;
380 attributes [attributeIndex].SchemaType = schemaType;
381 attributes [attributeIndex].LineNumber = lineNumber;
382 attributes [attributeIndex].LinePosition = linePosition;
385 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
386 public void AddNsNode (int declaredElement, string name, string ns)
388 if (namespaces.Length < nsIndex + 1) {
390 SetNsArrayLength (nsCapacity);
393 DTMXPathNamespaceNode nsNode = namespaces [nsIndex];
394 namespaces [nsIndex].DeclaredElement = declaredElement;
395 namespaces [nsIndex].Name = name;
396 namespaces [nsIndex].Namespace = ns;