Peter had a reason for this change; restore it (it was reverted in revision 1.4).
[mono.git] / mcs / class / System.XML / Mono.Xml.XPath / DTMXPathDocumentBuilder.cs
1 //
2 // Mono.Xml.XPath.DTMXPathDocumentBuilder
3 //
4 // Author:
5 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C) 2003 Atsushi Enomoto
8 //
9 using System;
10 using System.Collections;
11 using System.IO;
12 using System.Xml;
13 using System.Xml.Schema;
14 using System.Xml.XPath;
15
16 namespace Mono.Xml.XPath
17 {
18
19         public class DTMXPathDocumentBuilder
20         {
21                 public DTMXPathDocumentBuilder (string url)
22                         : this (url, XmlSpace.None, false)
23                 {
24                 }
25
26                 public DTMXPathDocumentBuilder (string url, XmlSpace space)
27                         : this (url, space, false)
28                 {
29                 }
30
31                 public DTMXPathDocumentBuilder (string url, XmlSpace space, bool supportID)
32                         : this (new XmlTextReader (url), space, supportID)
33                 {
34                 }
35
36                 public DTMXPathDocumentBuilder (XmlReader reader)
37                         : this (reader, XmlSpace.None, false)
38                 {
39                 }
40
41                 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space)
42                         : this (reader, space, false)
43                 {
44                 }
45
46                 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space, bool supportID)
47                 {
48                         this.xmlReader = reader;
49                         if (supportID)
50                                 this.validatingReader = reader as XmlValidatingReader;
51                         this.xmlSpace = xmlSpace;
52                         this.nameTable = reader.NameTable;
53                         Compile ();
54                 }
55                 
56                 bool supportID;
57                 XmlReader xmlReader;
58                 XmlValidatingReader validatingReader;
59                 XmlSpace xmlSpace;
60                 XmlNameTable nameTable;
61                 int defaultCapacity = 100;
62                 public int DefaultCapacity {
63                         get { return defaultCapacity; }
64                         set {
65                                 if (value < 0)
66                                         throw new ArgumentOutOfRangeException ();
67                                 defaultCapacity = value;
68                         }
69                 }
70
71 #region Tree node info collections.
72                 // Tree Node
73                 int [] firstChild_ = new int [0];
74                 int [] parent_ = new int [0];
75                 int [] firstAttribute_ = new int [0];
76                 int [] previousSibling_ = new int [0];
77                 int [] nextSibling_ = new int [0];
78                 int [] depth_ = new int [0];
79                 int [] position_ = new int [0];
80                 XPathNodeType [] nodeType_ = new XPathNodeType [0];
81                 string [] baseUri_ = new string [0];
82                 bool [] isEmptyElement_ = new bool [0];
83                 string [] localName_ = new string [0];
84                 string [] namespaceUri_ = new string [0];
85                 string [] prefix_ = new string [0];
86                 string [] value_ = new string [0];
87                 string [] xmlLang_ = new string [0];
88                 int [] namespaceNode_ = new int [0];
89                 object [] schemaType_ = new object [0];
90
91                 // Attribute
92                 int [] ownerElement_ = new int [0];
93                 int [] nextAttribute_ = new int [0];
94                 string [] attrLocalName_ = new string [0];
95                 string [] attrPrefix_ = new string [0];
96                 string [] attrNsUri_ = new string [0];
97                 string [] attrValue_ = new string [0];
98                 object [] attrSchemaType_ = new object [0];
99
100                 // NamespaceNode
101                 int [] nsDeclaredElement_ = new int [100];
102                 int [] nextNsNode_ = new int [100];
103                 string [] nsNodeName_ = new string [100];
104                 string [] nsNodeUri_ = new string [100];
105
106                 // idTable [string value] -> int nodeId
107                 Hashtable idTable_;
108 #endregion
109
110                 int nodeIndex;
111                 int attributeIndex;
112                 int nsIndex;
113                 bool requireFirstChildFill;
114
115                 public DTMXPathDocument CreateDocument ()
116                 {
117                         return new DTMXPathDocument (nameTable,
118                                 firstChild_,
119                                 parent_,
120                                 firstAttribute_,
121                                 previousSibling_,
122                                 nextSibling_,
123                                 depth_,
124                                 position_,
125                                 nodeType_,
126                                 baseUri_,
127                                 isEmptyElement_,
128                                 localName_,
129                                 namespaceUri_,
130                                 prefix_,
131                                 value_,
132                                 xmlLang_,
133                                 namespaceNode_,
134                                 schemaType_,
135
136                                 // Attribute
137                                 ownerElement_,
138                                 nextAttribute_,
139                                 attrLocalName_,
140                                 attrPrefix_,
141                                 attrNsUri_,
142                                 attrValue_,
143                                 attrSchemaType_,
144
145                                 // NamespaceNode
146                                 nsDeclaredElement_,
147                                 nextNsNode_,
148                                 nsNodeName_,
149                                 nsNodeUri_,
150                                 idTable_
151                         );
152                 }
153
154                 public void Compile ()
155                 {
156                         idTable_ = new Hashtable ();
157
158                         // index 0 is dummy. No node (including Root) is assigned to this index
159                         // So that we can easily compare index != 0 instead of index < 0.
160                         // (Difference between jnz or jbe in 80x86.)
161                         AddNode (0, 0, 0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, null);
162                         nodeIndex++;
163                         AddAttribute (0, null, null, null, null, null);
164                         nextAttribute_ [0] = 0;
165                         AddNsNode (0, null, null);
166                         nsIndex++;
167                         nextNsNode_ [0] = 0;
168                         AddNsNode (1, "xml", XmlNamespaces.XML);
169                         nextNsNode_ [1] = 0;
170
171                         // add root.
172                         AddNode (0, 0, 0, 0, -1, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, null);
173
174                         this.nodeIndex = 1;
175                         this.requireFirstChildFill = true;
176
177                         while (!xmlReader.EOF)
178                                 Read ();
179                         SetNodeArraysLength (nodeIndex + 1);
180                         SetAttributeArraysLength (attributeIndex + 1);
181                         SetNsArraysLength (nsIndex + 1);
182
183                         xmlReader = null;       // It is no more required.
184                 }
185
186                 int prevSibling;
187                 int position;
188                 bool skipRead = false;
189
190                 public void Read ()
191                 {
192                         if (!skipRead)
193                                 if (!xmlReader.Read ())
194                                         return;
195                         skipRead = false;
196                         int parent = nodeIndex;
197
198                         if (depth_ [nodeIndex] >= xmlReader.Depth) {    // not ">=" ? But == worked when with ArrayList...
199                                 // if not, then current node is parent.
200                                 while (xmlReader.Depth <= depth_ [parent])
201                                         parent = parent_ [parent];
202                         }
203
204                         prevSibling = nodeIndex;
205                         position = 0;
206                         switch (xmlReader.NodeType) {
207                         case XmlNodeType.Element:
208                         case XmlNodeType.CDATA:
209                         case XmlNodeType.SignificantWhitespace:
210                         case XmlNodeType.Comment:
211                         case XmlNodeType.Text:
212                         case XmlNodeType.ProcessingInstruction:
213                                 if (requireFirstChildFill)
214                                         prevSibling = 0;
215                                 else
216                                         while (depth_ [prevSibling] != xmlReader.Depth)
217                                                 prevSibling = parent_ [prevSibling];
218                                 if (prevSibling != 0)
219                                         position = position_ [prevSibling] + 1;
220
221                                 nodeIndex++;
222
223                                 if (prevSibling != 0)
224                                         nextSibling_ [prevSibling] = nodeIndex;
225                                 if (requireFirstChildFill)
226                                         firstChild_ [parent] = nodeIndex;
227                                 break;
228                         case XmlNodeType.Whitespace:
229                                 if (xmlSpace == XmlSpace.Preserve)
230                                         goto case XmlNodeType.Text;
231                                 else
232                                         goto default;
233                         case XmlNodeType.EndElement:
234                                 requireFirstChildFill = false;
235                                 return;
236                         default:
237                                 // No operations. Doctype, EntityReference, 
238                                 return;
239                         }
240
241                         requireFirstChildFill = false;  // Might be changed in ProcessElement().
242
243                         string value = null;
244                         XPathNodeType nodeType = XPathNodeType.Text;
245
246                         switch (xmlReader.NodeType) {
247                         case XmlNodeType.Element:
248                                 ProcessElement (parent, prevSibling, position);
249                                 break;
250                         case XmlNodeType.CDATA:
251                         case XmlNodeType.SignificantWhitespace:
252                         case XmlNodeType.Text:
253                                 if (value == null)
254                                         skipRead = true;
255                                 AddNode (parent,
256                                         0,
257                                         attributeIndex,
258                                         prevSibling,
259                                         xmlReader.Depth,
260                                         position,
261                                         nodeType,
262                                         xmlReader.BaseURI,
263                                         xmlReader.IsEmptyElement,
264                                         xmlReader.LocalName,    // for PI
265                                         xmlReader.NamespaceURI, // for PI
266                                         xmlReader.Prefix,
267                                         value,
268                                         xmlReader.XmlLang,
269                                         nsIndex,
270                                         null);  // schemaType
271                                 // this code is tricky, but after ReadString() invokation,
272                                 // xmlReader is moved to next node!!
273                                 if (value == null)
274                                         value_ [nodeIndex] = xmlReader.ReadString ();
275                                 break;
276                         case XmlNodeType.Comment:
277                                 value = xmlReader.Value;
278                                 nodeType = XPathNodeType.Comment;
279                                 goto case XmlNodeType.Text;
280                         case XmlNodeType.Whitespace:
281                                 goto case XmlNodeType.Text;
282                         case XmlNodeType.ProcessingInstruction:
283                                 value = xmlReader.Value;
284                                 nodeType = XPathNodeType.ProcessingInstruction;
285                                 goto case XmlNodeType.Text;
286                         }
287                 }
288
289                 private void ProcessElement (int parent, int previousSibling, int position)
290                 {
291                         int firstAttributeIndex = 0;
292                         int lastNsIndexInCurrent = 0;
293
294
295                         // process namespaces and attributes.
296                         if (xmlReader.MoveToFirstAttribute ()) {
297                                 do {
298                                         if (xmlReader.NamespaceURI == XmlNamespaces.XMLNS) {
299                                                 // add namespace node.
300                                                 nsIndex++;
301
302                                                 nextNsNode_ [nsIndex] = lastNsIndexInCurrent == 0 ? namespaceNode_ [parent] : lastNsIndexInCurrent;
303
304                                                 if (lastNsIndexInCurrent == 0)
305                                                         namespaceNode_ [nodeIndex] = nsIndex;
306                                                 this.AddNsNode (nodeIndex,
307                                                         (xmlReader.Prefix == null || xmlReader.Prefix == String.Empty) ?
308                                                                 "" : xmlReader.LocalName,
309                                                         xmlReader.Value);
310                                                 lastNsIndexInCurrent = nsIndex;
311                                         } else {
312                                                 // add attribute node.
313                                                 attributeIndex ++;
314                                                 this.AddAttribute (nodeIndex, xmlReader.LocalName, xmlReader.NamespaceURI, xmlReader.Prefix != null ? xmlReader.Prefix : String.Empty, xmlReader.Value, null);
315                                                 if (firstAttributeIndex == 0)
316                                                         firstAttributeIndex = attributeIndex;
317                                                 else
318                                                         nextAttribute_ [attributeIndex - 1] = attributeIndex;
319                                                 // dummy for "current" attribute.
320                                                 nextAttribute_ [attributeIndex] = 0;
321
322                                                 // Identity infoset
323                                                 if (validatingReader != null) {
324                                                         XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
325                                                         if (dt == null) {
326                                                                 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
327                                                                 dt = xsType.Datatype;
328                                                         }
329                                                         if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
330                                                                 idTable_.Add (xmlReader.Value, nodeIndex);
331                                                 }
332                                         }
333                                 } while (xmlReader.MoveToNextAttribute ());
334                                 xmlReader.MoveToElement ();
335                         }
336
337                         AddNode (parent,
338                                 firstAttributeIndex,
339                                 attributeIndex,
340                                 previousSibling,
341                                 xmlReader.Depth,
342                                 position,
343                                 XPathNodeType.Element,
344                                 xmlReader.BaseURI,
345                                 xmlReader.IsEmptyElement,
346                                 xmlReader.LocalName,
347                                 xmlReader.NamespaceURI,
348                                 xmlReader.Prefix,
349                                 "",     // Element has no internal value.
350                                 xmlReader.XmlLang,
351                                 nsIndex,
352                                 null);  // schemaType
353                         if (!xmlReader.IsEmptyElement)
354                                 requireFirstChildFill = true;
355                 }
356
357                 private void SetObjectArrayLength (ref object [] a, int length)
358                 {
359                         object [] arr = new object [length];
360                         Array.Copy (a, arr, System.Math.Min (a.Length, length));
361                         a = arr;
362                 }
363
364                 private void SetBoolArrayLength (ref bool [] a, int length)
365                 {
366                         bool [] bArr = new bool [length];
367                         Array.Copy (a, bArr, System.Math.Min (a.Length, length));
368                         a = bArr;
369                 }
370
371                 private void SetXPathNodeTypeArrayLength (ref XPathNodeType [] a, int length)
372                 {
373                         XPathNodeType [] arr = new XPathNodeType [length];
374                         Array.Copy (a, arr, System.Math.Min (a.Length, length));
375                         a = arr;
376                 }
377
378                 private void SetIntArrayLength (ref int [] a, int length)
379                 {
380                         int [] intArr = new int [length];
381                         Array.Copy (a, intArr, System.Math.Min (a.Length, length));
382                         a = intArr;
383                 }
384
385                 private void SetStringArrayLength (ref string [] a, int length)
386                 {
387                         string [] strArr = new string [length];
388                         Array.Copy (a, strArr, System.Math.Min (a.Length, length));
389                         a = strArr;
390                 }
391
392                 private void SetNodeArraysLength (int size)
393                 {
394                         SetIntArrayLength (ref firstChild_, size);
395                         SetIntArrayLength (ref parent_, size);
396                         SetIntArrayLength (ref firstAttribute_, size);
397                         SetIntArrayLength (ref previousSibling_, size);
398                         SetIntArrayLength (ref nextSibling_, size);
399                         SetIntArrayLength (ref depth_, size);
400                         SetIntArrayLength (ref position_, size);
401                         SetXPathNodeTypeArrayLength (ref nodeType_, size);
402                         SetStringArrayLength (ref baseUri_, size);
403                         SetBoolArrayLength (ref isEmptyElement_, size);
404                         SetStringArrayLength (ref localName_, size);
405                         SetStringArrayLength (ref namespaceUri_, size);
406                         SetStringArrayLength (ref prefix_, size);
407                         SetStringArrayLength (ref value_, size);
408                         SetStringArrayLength (ref xmlLang_, size);
409                         SetIntArrayLength (ref namespaceNode_, size);
410                         SetObjectArrayLength (ref schemaType_, size);
411                 }
412
413                 private void SetAttributeArraysLength (int size)
414                 {
415                         SetIntArrayLength (ref ownerElement_, size);
416                         SetIntArrayLength (ref nextAttribute_, size);
417                         SetStringArrayLength (ref attrLocalName_, size);
418                         SetStringArrayLength (ref attrPrefix_, size);
419                         SetStringArrayLength (ref attrNsUri_, size);
420                         SetStringArrayLength (ref attrValue_, size);
421                         SetObjectArrayLength (ref attrSchemaType_, size);
422                 }
423
424                 private void SetNsArraysLength (int size)
425                 {
426                         SetIntArrayLength (ref nsDeclaredElement_, size);
427                         SetIntArrayLength (ref nextNsNode_, size);
428                         SetStringArrayLength (ref nsNodeName_, size);
429                         SetStringArrayLength (ref nsNodeUri_, size);
430                 }
431
432                 // Here followings are skipped: firstChild, nextSibling, 
433                 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, object schemaType)
434                 {
435                         if (firstChild_.Length < nodeIndex + 1) {
436                                 if (firstChild_.Length >= defaultCapacity)
437                                         defaultCapacity *= 2;
438                                 SetNodeArraysLength (defaultCapacity);
439                         }
440
441                         firstChild_ [nodeIndex] = 0;            // dummy
442                         parent_ [nodeIndex] = parent;
443                         firstAttribute_ [nodeIndex] = firstAttribute;
444                         previousSibling_ [nodeIndex] = previousSibling;
445                         nextSibling_ [nodeIndex] = 0;   // dummy
446                         depth_ [nodeIndex] = depth;
447                         position_ [nodeIndex] = position;
448                         nodeType_ [nodeIndex] = nodeType;
449                         baseUri_ [nodeIndex] = baseUri;
450                         isEmptyElement_ [nodeIndex] = isEmptyElement;
451                         localName_ [nodeIndex] = localName;
452                         namespaceUri_ [nodeIndex] = ns;
453                         prefix_ [nodeIndex] = prefix;
454                         value_ [nodeIndex] = value;
455                         xmlLang_ [nodeIndex] = xmlLang;
456                         namespaceNode_ [nodeIndex] = namespaceNode;
457                         schemaType_ [nodeIndex] = schemaType;
458                 }
459
460                 // Followings are skipped: nextAttribute,
461                 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType)
462                 {
463                         if (ownerElement_.Length < attributeIndex + 1) {
464                                 if (ownerElement_.Length >= defaultCapacity)
465                                         defaultCapacity *= 2;
466                                 SetAttributeArraysLength (defaultCapacity);
467                         }
468
469                         ownerElement_ [attributeIndex] = ownerElement;
470                         attrLocalName_ [attributeIndex] = localName;
471                         attrNsUri_ [attributeIndex] = ns;
472                         attrPrefix_ [attributeIndex] = prefix;
473                         attrValue_ [attributeIndex] = value;
474                         attrSchemaType_ [attributeIndex] = schemaType;
475                 }
476
477                 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
478                 public void AddNsNode (int declaredElement, string name, string ns)
479                 {
480                         if (nsDeclaredElement_.Length < nsIndex + 1) {
481                                 if (nsDeclaredElement_.Length >= defaultCapacity)
482                                         defaultCapacity *= 2;
483                                 SetNsArraysLength (defaultCapacity);
484                         }
485
486                         nsDeclaredElement_ [nsIndex] = declaredElement;
487                         nsNodeName_ [nsIndex] = name;
488                         nsNodeUri_ [nsIndex] = ns;
489                 }
490         }
491 }
492