2 // Mono.Xml.XPath.DTMXPathDocumentBuilder
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C) 2003 Atsushi Enomoto
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
35 using System.Xml.Schema;
36 using System.Xml.XPath;
38 namespace Mono.Xml.XPath
41 #if OUTSIDE_SYSTEM_XML
46 class DTMXPathDocumentBuilder
48 public DTMXPathDocumentBuilder (string url)
49 : this (url, XmlSpace.None, 200)
53 public DTMXPathDocumentBuilder (string url, XmlSpace space)
54 : this (url, space, 200)
58 public DTMXPathDocumentBuilder (string url, XmlSpace space, int defaultCapacity)
62 r = new XmlTextReader (url);
63 Init (r, space, defaultCapacity);
70 public DTMXPathDocumentBuilder (XmlReader reader)
71 : this (reader, XmlSpace.None, 200)
75 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space)
76 : this (reader, space, 200)
80 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space, int defaultCapacity)
82 Init (reader, space, defaultCapacity);
85 private void Init (XmlReader reader, XmlSpace space, int defaultCapacity)
87 this.xmlReader = reader;
88 this.validatingReader = reader as XmlValidatingReader;
89 lineInfo = reader as IXmlLineInfo;
90 this.xmlSpace = space;
91 this.nameTable = reader.NameTable;
92 nodeCapacity = defaultCapacity;
93 attributeCapacity = nodeCapacity;
94 idTable = new Hashtable ();
96 nodes = new DTMXPathLinkedNode [nodeCapacity];
97 attributes = new DTMXPathAttributeNode [attributeCapacity];
98 namespaces = new DTMXPathNamespaceNode [0];
104 XmlValidatingReader validatingReader;
106 XmlNameTable nameTable;
107 IXmlLineInfo lineInfo;
108 int nodeCapacity = 200;
109 int attributeCapacity = 200;
113 DTMXPathLinkedNode [] nodes;
116 DTMXPathAttributeNode [] attributes;
119 DTMXPathNamespaceNode [] namespaces;
121 // idTable [string value] -> int nodeId
127 int parentForFirstChild;
129 // for attribute processing; should be reset per each element.
130 int firstAttributeIndex;
131 int lastNsIndexInCurrent;
132 int attrIndexAtStart;
137 bool skipRead = false;
139 public DTMXPathDocument CreateDocument ()
141 return new DTMXPathDocument (nameTable,
149 public void Compile ()
151 // index 0 is dummy. No node (including Root) is assigned to this index
152 // So that we can easily compare index != 0 instead of index < 0.
153 // (Difference between jnz or jbe in 80x86.)
154 AddNode (0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
156 AddAttribute (0, null, null, null, null, 0, 0);
157 AddNsNode (0, null, null, 0);
159 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
162 AddNode (0, 0, 0, -1, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
165 this.lastNsInScope = 1;
166 this.parentForFirstChild = nodeIndex;
168 while (!xmlReader.EOF)
170 SetNodeArrayLength (nodeIndex + 1);
171 SetAttributeArrayLength (attributeIndex + 1);
172 SetNsArrayLength (nsIndex + 1);
174 xmlReader = null; // It is no more required.
180 if (!xmlReader.Read ())
183 int parent = nodeIndex;
185 if (nodes [nodeIndex].Depth >= xmlReader.Depth) {
186 // if not, then current node is parent.
187 while (xmlReader.Depth <= nodes [parent].Depth)
188 parent = nodes [parent].Parent;
191 prevSibling = nodeIndex;
192 switch (xmlReader.NodeType) {
193 case XmlNodeType.Element:
194 case XmlNodeType.CDATA:
195 case XmlNodeType.SignificantWhitespace:
196 case XmlNodeType.Comment:
197 case XmlNodeType.Text:
198 case XmlNodeType.ProcessingInstruction:
199 if (parentForFirstChild >= 0)
202 while (nodes [prevSibling].Depth != xmlReader.Depth)
203 prevSibling = nodes [prevSibling].Parent;
207 if (prevSibling != 0)
208 nodes [prevSibling].NextSibling = nodeIndex;
209 if (parentForFirstChild >= 0)
210 nodes [parent].FirstChild = nodeIndex;
212 case XmlNodeType.Whitespace:
213 if (xmlSpace == XmlSpace.Preserve)
214 goto case XmlNodeType.Text;
217 case XmlNodeType.EndElement:
218 parentForFirstChild = -1;
221 // No operations. Doctype, EntityReference,
225 parentForFirstChild = -1; // Might be changed in ProcessElement().
228 XPathNodeType nodeType = xmlReader.NodeType == XmlNodeType.Whitespace ?
229 XPathNodeType.Whitespace : XPathNodeType.Text;
231 switch (xmlReader.NodeType) {
232 case XmlNodeType.Element:
233 ProcessElement (parent, prevSibling);
235 case XmlNodeType.CDATA:
236 case XmlNodeType.SignificantWhitespace:
237 case XmlNodeType.Text:
238 case XmlNodeType.Whitespace:
247 xmlReader.IsEmptyElement,
248 xmlReader.LocalName, // for PI
249 xmlReader.NamespaceURI, // for PI
254 lineInfo != null ? lineInfo.LineNumber : 0,
255 lineInfo != null ? lineInfo.LinePosition : 0);
256 // this code is tricky, but after ReadString() invokation,
257 // xmlReader is moved to next node!!
259 nodes [nodeIndex].Value = xmlReader.ReadString ();
261 case XmlNodeType.Comment:
262 value = xmlReader.Value;
263 nodeType = XPathNodeType.Comment;
264 goto case XmlNodeType.Text;
265 case XmlNodeType.ProcessingInstruction:
266 value = xmlReader.Value;
267 nodeType = XPathNodeType.ProcessingInstruction;
268 goto case XmlNodeType.Text;
272 private void ProcessElement (int parent, int previousSibling)
274 WriteStartElement (parent, previousSibling);
276 // process namespaces and attributes.
277 if (xmlReader.MoveToFirstAttribute ()) {
279 string prefix = xmlReader.Prefix;
280 string ns = xmlReader.NamespaceURI;
281 if (ns == XmlNamespaces.XMLNS)
282 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : xmlReader.LocalName, xmlReader.Value);
284 ProcessAttribute (prefix, xmlReader.LocalName, ns, xmlReader.Value);
286 } while (xmlReader.MoveToNextAttribute ());
287 xmlReader.MoveToElement ();
290 CloseStartElement ();
293 private void PrepareStartElement (int previousSibling)
295 firstAttributeIndex = 0;
296 lastNsIndexInCurrent = 0;
297 attrIndexAtStart = attributeIndex;
298 nsIndexAtStart = nsIndex;
300 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
301 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
305 private void WriteStartElement (int parent, int previousSibling)
307 PrepareStartElement (previousSibling);
310 0, // dummy:firstAttribute
313 XPathNodeType.Element,
315 xmlReader.IsEmptyElement,
317 xmlReader.NamespaceURI,
319 "", // Element has no internal value.
322 lineInfo != null ? lineInfo.LineNumber : 0,
323 lineInfo != null ? lineInfo.LinePosition : 0);
327 private void CloseStartElement ()
329 if (attrIndexAtStart != attributeIndex)
330 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
331 if (nsIndexAtStart != nsIndex) {
332 nodes [nodeIndex].FirstNamespace = nsIndex;
333 lastNsInScope = nsIndex;
336 if (!nodes [nodeIndex].IsEmptyElement)
337 parentForFirstChild = nodeIndex;
340 private void ProcessNamespace (string prefix, string ns)
344 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
346 this.AddNsNode (nodeIndex,
350 lastNsIndexInCurrent = nsIndex;
353 private void ProcessAttribute (string prefix, string localName, string ns, string value)
357 this.AddAttribute (nodeIndex,
360 prefix != null ? prefix : String.Empty,
362 lineInfo != null ? lineInfo.LineNumber : 0,
363 lineInfo != null ? lineInfo.LinePosition : 0);
364 if (firstAttributeIndex == 0)
365 firstAttributeIndex = attributeIndex;
367 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
370 if (validatingReader != null) {
371 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
373 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
375 dt = xsType.Datatype;
377 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
378 idTable.Add (value, nodeIndex);
382 private void SetNodeArrayLength (int size)
384 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
385 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
389 private void SetAttributeArrayLength (int size)
391 DTMXPathAttributeNode [] newArr =
392 new DTMXPathAttributeNode [size];
393 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
397 private void SetNsArrayLength (int size)
399 DTMXPathNamespaceNode [] newArr =
400 new DTMXPathNamespaceNode [size];
401 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
405 // Here followings are skipped: firstChild, nextSibling,
406 public void AddNode (int parent, int firstAttribute, int previousSibling, int depth, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
408 if (nodes.Length < nodeIndex + 1) {
410 SetNodeArrayLength (nodeCapacity);
414 nodes [nodeIndex] = new DTMXPathLinkedNode ();
416 nodes [nodeIndex].FirstChild = 0; // dummy
417 nodes [nodeIndex].Parent = parent;
418 nodes [nodeIndex].FirstAttribute = firstAttribute;
419 nodes [nodeIndex].PreviousSibling = previousSibling;
420 nodes [nodeIndex].NextSibling = 0; // dummy
421 nodes [nodeIndex].Depth = depth;
422 nodes [nodeIndex].NodeType = nodeType;
423 nodes [nodeIndex].BaseURI = baseUri;
424 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
425 nodes [nodeIndex].LocalName = localName;
426 nodes [nodeIndex].NamespaceURI = ns;
427 nodes [nodeIndex].Prefix = prefix;
428 nodes [nodeIndex].Value = value;
429 nodes [nodeIndex].XmlLang = xmlLang;
430 nodes [nodeIndex].FirstNamespace = namespaceNode;
431 nodes [nodeIndex].LineNumber = lineNumber;
432 nodes [nodeIndex].LinePosition = linePosition;
435 // Followings are skipped: nextAttribute,
436 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
438 if (attributes.Length < attributeIndex + 1) {
439 attributeCapacity *= 4;
440 SetAttributeArrayLength (attributeCapacity);
444 attributes [attributeIndex] = new DTMXPathAttributeNode ();
446 attributes [attributeIndex].OwnerElement = ownerElement;
447 attributes [attributeIndex].LocalName = localName;
448 attributes [attributeIndex].NamespaceURI = ns;
449 attributes [attributeIndex].Prefix = prefix;
450 attributes [attributeIndex].Value = value;
451 attributes [attributeIndex].LineNumber = lineNumber;
452 attributes [attributeIndex].LinePosition = linePosition;
455 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
456 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
458 if (namespaces.Length < nsIndex + 1) {
460 SetNsArrayLength (nsCapacity);
464 namespaces [nsIndex] = new DTMXPathNamespaceNode ();
466 namespaces [nsIndex].DeclaredElement = declaredElement;
467 namespaces [nsIndex].Name = name;
468 namespaces [nsIndex].Namespace = ns;
469 namespaces [nsIndex].NextNamespace = nextNs;