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