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;
95 idTable = new Hashtable ();
97 nodes = new DTMXPathLinkedNode [nodeCapacity];
98 attributes = new DTMXPathAttributeNode [attributeCapacity];
99 namespaces = new DTMXPathNamespaceNode [nsCapacity];
105 XmlValidatingReader validatingReader;
107 XmlNameTable nameTable;
108 IXmlLineInfo lineInfo;
110 int attributeCapacity;
114 DTMXPathLinkedNode [] nodes;
117 DTMXPathAttributeNode [] attributes;
120 DTMXPathNamespaceNode [] namespaces;
122 // idTable [string value] -> int nodeId
129 // for attribute processing; should be reset per each element.
132 int attrIndexAtStart;
136 bool skipRead = false;
138 int [] parentStack = new int [10];
139 int parentStackIndex = 0;
141 public DTMXPathDocument CreateDocument ()
143 return new DTMXPathDocument (nameTable,
151 public void Compile ()
153 // index 0 is dummy. No node (including Root) is assigned to this index
154 // So that we can easily compare index != 0 instead of index < 0.
155 // (Difference between jnz or jbe in 80x86.)
156 AddNode (0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
158 AddAttribute (0, null, null, null, null, 0, 0);
159 AddNsNode (0, null, null, 0);
161 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
164 AddNode (0, 0, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
167 this.lastNsInScope = 1;
168 parentStack [0] = nodeIndex;
170 while (!xmlReader.EOF)
172 SetNodeArrayLength (nodeIndex + 1);
173 SetAttributeArrayLength (attributeIndex + 1);
174 SetNsArrayLength (nsIndex + 1);
176 xmlReader = null; // It is no more required.
182 if (!xmlReader.Read ())
185 int parent = parentStack [parentStackIndex];
186 int prevSibling = nodeIndex;
188 switch (xmlReader.NodeType) {
189 case XmlNodeType.Element:
190 case XmlNodeType.CDATA:
191 case XmlNodeType.SignificantWhitespace:
192 case XmlNodeType.Comment:
193 case XmlNodeType.Text:
194 case XmlNodeType.ProcessingInstruction:
195 if (parent == nodeIndex)
198 while (nodes [prevSibling].Parent != parent)
199 prevSibling = nodes [prevSibling].Parent;
203 if (prevSibling != 0)
204 nodes [prevSibling].NextSibling = nodeIndex;
205 if (parentStack [parentStackIndex] == nodeIndex - 1)
206 nodes [parent].FirstChild = nodeIndex;
208 case XmlNodeType.Whitespace:
209 if (xmlSpace == XmlSpace.Preserve)
210 goto case XmlNodeType.Text;
213 case XmlNodeType.EndElement:
217 // No operations. Doctype, EntityReference,
222 XPathNodeType nodeType = XPathNodeType.Text;
224 switch (xmlReader.NodeType) {
225 case XmlNodeType.Element:
226 ProcessElement (parent, prevSibling);
228 case XmlNodeType.SignificantWhitespace:
229 nodeType = XPathNodeType.SignificantWhitespace;
230 goto case XmlNodeType.Text;
231 case XmlNodeType.Whitespace:
232 nodeType = XPathNodeType.Whitespace;
233 goto case XmlNodeType.Text;
234 case XmlNodeType.CDATA:
235 case XmlNodeType.Text:
241 xmlReader.IsEmptyElement,
242 xmlReader.LocalName, // for PI
243 xmlReader.NamespaceURI, // for PI
248 lineInfo != null ? lineInfo.LineNumber : 0,
249 lineInfo != null ? lineInfo.LinePosition : 0);
250 // this code is tricky, but after sequential
251 // Read() invokation, xmlReader is moved to
255 value = String.Empty;
256 XPathNodeType type = XPathNodeType.Whitespace;
258 switch (xmlReader.NodeType) {
259 case XmlNodeType.Text:
260 case XmlNodeType.CDATA:
261 type = XPathNodeType.Text;
262 goto case XmlNodeType.Whitespace;
263 case XmlNodeType.SignificantWhitespace:
264 if (type == XPathNodeType.Whitespace)
265 type = XPathNodeType.SignificantWhitespace;
266 goto case XmlNodeType.Whitespace;
267 case XmlNodeType.Whitespace:
268 if (xmlReader.NodeType != XmlNodeType.Whitespace || xmlSpace == XmlSpace.Preserve)
269 value += xmlReader.Value;
270 loop = xmlReader.Read ();
278 nodes [nodeIndex].Value = value;
279 nodes [nodeIndex].NodeType = type;
282 case XmlNodeType.Comment:
283 value = xmlReader.Value;
284 nodeType = XPathNodeType.Comment;
285 goto case XmlNodeType.Text;
286 case XmlNodeType.ProcessingInstruction:
287 value = xmlReader.Value;
288 nodeType = XPathNodeType.ProcessingInstruction;
289 goto case XmlNodeType.Text;
293 private void ProcessElement (int parent, int previousSibling)
295 WriteStartElement (parent, previousSibling);
297 // process namespaces and attributes.
298 if (xmlReader.MoveToFirstAttribute ()) {
300 string prefix = xmlReader.Prefix;
301 string ns = xmlReader.NamespaceURI;
302 if (ns == XmlNamespaces.XMLNS)
303 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : xmlReader.LocalName, xmlReader.Value);
305 ProcessAttribute (prefix, xmlReader.LocalName, ns, xmlReader.Value);
307 } while (xmlReader.MoveToNextAttribute ());
308 xmlReader.MoveToElement ();
311 CloseStartElement ();
314 private void PrepareStartElement (int previousSibling)
316 hasAttributes = false;
318 attrIndexAtStart = attributeIndex;
319 nsIndexAtStart = nsIndex;
321 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
322 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
326 private void WriteStartElement (int parent, int previousSibling)
328 PrepareStartElement (previousSibling);
331 0, // dummy:firstAttribute
333 XPathNodeType.Element,
335 xmlReader.IsEmptyElement,
337 xmlReader.NamespaceURI,
339 "", // Element has no internal value.
342 lineInfo != null ? lineInfo.LineNumber : 0,
343 lineInfo != null ? lineInfo.LinePosition : 0);
347 private void CloseStartElement ()
349 if (attrIndexAtStart != attributeIndex)
350 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
351 if (nsIndexAtStart != nsIndex) {
352 nodes [nodeIndex].FirstNamespace = nsIndex;
353 if (!xmlReader.IsEmptyElement)
354 lastNsInScope = nsIndex;
357 if (!nodes [nodeIndex].IsEmptyElement) {
359 if (parentStack.Length == parentStackIndex) {
360 int [] tmp = new int [parentStackIndex * 2];
361 Array.Copy (parentStack, tmp, parentStackIndex);
364 parentStack [parentStackIndex] = nodeIndex;
368 private void ProcessNamespace (string prefix, string ns)
370 int nextTmp = hasLocalNs ?
371 nsIndex : nodes [nodeIndex].FirstNamespace;
375 this.AddNsNode (nodeIndex,
382 private void ProcessAttribute (string prefix, string localName, string ns, string value)
386 this.AddAttribute (nodeIndex,
389 prefix != null ? prefix : String.Empty,
391 lineInfo != null ? lineInfo.LineNumber : 0,
392 lineInfo != null ? lineInfo.LinePosition : 0);
394 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
396 hasAttributes = true;
399 if (validatingReader != null) {
400 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
402 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
404 dt = xsType.Datatype;
406 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
407 idTable.Add (value, nodeIndex);
411 private void SetNodeArrayLength (int size)
413 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
414 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
418 private void SetAttributeArrayLength (int size)
420 DTMXPathAttributeNode [] newArr =
421 new DTMXPathAttributeNode [size];
422 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
426 private void SetNsArrayLength (int size)
428 DTMXPathNamespaceNode [] newArr =
429 new DTMXPathNamespaceNode [size];
430 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
434 // Here followings are skipped: firstChild, nextSibling,
435 public void AddNode (int parent, int firstAttribute, int previousSibling, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
437 if (nodes.Length < nodeIndex + 1) {
439 SetNodeArrayLength (nodeCapacity);
443 nodes [nodeIndex] = new DTMXPathLinkedNode ();
445 nodes [nodeIndex].FirstChild = 0; // dummy
446 nodes [nodeIndex].Parent = parent;
447 nodes [nodeIndex].FirstAttribute = firstAttribute;
448 nodes [nodeIndex].PreviousSibling = previousSibling;
449 nodes [nodeIndex].NextSibling = 0; // dummy
450 nodes [nodeIndex].NodeType = nodeType;
451 nodes [nodeIndex].BaseURI = baseUri;
452 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
453 nodes [nodeIndex].LocalName = localName;
454 nodes [nodeIndex].NamespaceURI = ns;
455 nodes [nodeIndex].Prefix = prefix;
456 nodes [nodeIndex].Value = value;
457 nodes [nodeIndex].XmlLang = xmlLang;
458 nodes [nodeIndex].FirstNamespace = namespaceNode;
459 nodes [nodeIndex].LineNumber = lineNumber;
460 nodes [nodeIndex].LinePosition = linePosition;
463 // Followings are skipped: nextAttribute,
464 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
466 if (attributes.Length < attributeIndex + 1) {
467 attributeCapacity *= 4;
468 SetAttributeArrayLength (attributeCapacity);
472 attributes [attributeIndex] = new DTMXPathAttributeNode ();
474 attributes [attributeIndex].OwnerElement = ownerElement;
475 attributes [attributeIndex].LocalName = localName;
476 attributes [attributeIndex].NamespaceURI = ns;
477 attributes [attributeIndex].Prefix = prefix;
478 attributes [attributeIndex].Value = value;
479 attributes [attributeIndex].LineNumber = lineNumber;
480 attributes [attributeIndex].LinePosition = linePosition;
483 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
484 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
486 if (namespaces.Length < nsIndex + 1) {
488 SetNsArrayLength (nsCapacity);
492 namespaces [nsIndex] = new DTMXPathNamespaceNode ();
494 namespaces [nsIndex].DeclaredElement = declaredElement;
495 namespaces [nsIndex].Name = name;
496 namespaces [nsIndex].Namespace = ns;
497 namespaces [nsIndex].NextNamespace = nextNs;