Merge pull request #2454 from tastywheattasteslikechicken/FixVtableAbort
[mono.git] / mcs / class / System.ServiceModel / Mono.Xml.XPath / DTMXPathDocumentBuilder2.cs
1 //
2 // Mono.Xml.XPath.DTMXPathDocumentBuilder2
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // (C)2004 Novell Inc.
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 #pragma warning disable 618
31
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Xml;
36 using System.Xml.Schema;
37 using System.Xml.XPath;
38
39 namespace Mono.Xml.XPath
40 {
41 #if OUTSIDE_SYSTEM_XML
42         public
43 #else
44         internal
45 #endif
46         class DTMXPathDocumentBuilder2
47         {
48                 public DTMXPathDocumentBuilder2 (string url)
49                         : this (url, XmlSpace.None, 200)
50                 {
51                 }
52
53                 public DTMXPathDocumentBuilder2 (string url, XmlSpace space)
54                         : this (url, space, 200)
55                 {
56                 }
57
58                 public DTMXPathDocumentBuilder2 (string url, XmlSpace space, int defaultCapacity)
59                 {
60                         XmlReader r = null;
61                         try {
62                                 r = new XmlTextReader (url);
63                                 Init (r, space, defaultCapacity);
64                         } finally {
65                                 if (r != null)
66                                         r.Close ();
67                         }
68                 }
69
70                 public DTMXPathDocumentBuilder2 (XmlReader reader)
71                         : this (reader, XmlSpace.None, 200)
72                 {
73                 }
74
75                 public DTMXPathDocumentBuilder2 (XmlReader reader, XmlSpace space)
76                         : this (reader, space, 200)
77                 {
78                 }
79
80                 public DTMXPathDocumentBuilder2 (XmlReader reader, XmlSpace space, int defaultCapacity)
81                 {
82                         Init (reader, space, defaultCapacity);
83                 }
84
85                 private void Init (XmlReader reader, XmlSpace space, int defaultCapacity)
86                 {
87                         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                         nsCapacity = 10;
95                         idTable = new Hashtable ();
96
97                         nodes = new DTMXPathLinkedNode2 [nodeCapacity];
98                         attributes = new DTMXPathAttributeNode2 [attributeCapacity];
99                         namespaces = new DTMXPathNamespaceNode2 [nsCapacity];
100                         atomicStringPool = new string [20];
101                         nonAtomicStringPool = new string [20];
102
103                         Compile ();
104                 }
105                 
106                 XmlReader xmlReader;
107                 XmlValidatingReader validatingReader;
108                 XmlSpace xmlSpace;
109                 XmlNameTable nameTable;
110                 IXmlLineInfo lineInfo;
111                 int nodeCapacity;
112                 int attributeCapacity;
113                 int nsCapacity;
114
115                 // Linked Node
116                 DTMXPathLinkedNode2 [] nodes;
117
118                 // Attribute
119                 DTMXPathAttributeNode2 [] attributes;
120
121                 // NamespaceNode
122                 DTMXPathNamespaceNode2 [] namespaces;
123
124                 // String pool
125                 string [] atomicStringPool;
126                 int atomicIndex;
127                 string [] nonAtomicStringPool;
128                 int nonAtomicIndex;
129
130                 // idTable [string value] -> int nodeId
131                 Hashtable idTable;
132
133                 int nodeIndex;
134                 int attributeIndex;
135                 int nsIndex;
136
137                 // for attribute processing; should be reset per each element.
138                 bool hasAttributes;
139                 bool hasLocalNs;
140                 int attrIndexAtStart;
141                 int nsIndexAtStart;
142
143                 int lastNsInScope;
144                 bool skipRead = false;
145
146                 int [] parentStack = new int [10];
147                 int parentStackIndex = 0;
148
149                 public DTMXPathDocument2 CreateDocument ()
150                 {
151                         return new DTMXPathDocument2 (nameTable,
152                                 nodes,
153                                 attributes,
154                                 namespaces,
155                                 atomicStringPool,
156                                 nonAtomicStringPool,
157                                 idTable
158                         );
159                 }
160
161                 public void Compile ()
162                 {
163                         // string pool index 0 to 3 are fixed.
164                         atomicStringPool [0] = nonAtomicStringPool [0] = "";
165                         atomicStringPool [1] = nonAtomicStringPool [1] = null;
166                         atomicStringPool [2] = nonAtomicStringPool [2] = XmlNamespaces.XML;
167                         atomicStringPool [3] = nonAtomicStringPool [3] = XmlNamespaces.XMLNS;
168                         atomicIndex = nonAtomicIndex = 4;
169
170                         // index 0 is dummy. No node (including Root) is assigned to this index
171                         // So that we can easily compare index != 0 instead of index < 0.
172                         // (Difference between jnz or jbe in 80x86.)
173                         AddNode (0, 0, 0, XPathNodeType.All, 0, false, 0, 0, 0, 0, 0, 0, 0, 0);
174                         nodeIndex++;
175                         AddAttribute (0, 0, 0, 0, 0, 0, 0);
176                         AddNsNode (0, 0, 0, 0);
177                         nsIndex++;
178                         AddNsNode (1, AtomicIndex ("xml"), AtomicIndex (XmlNamespaces.XML), 0);
179
180                         // add root.
181                         AddNode (0, 0, 0, XPathNodeType.Root, AtomicIndex (xmlReader.BaseURI), false, 0, 0, 0, 0, 0, 1, 0, 0);
182
183                         this.nodeIndex = 1;
184                         this.lastNsInScope = 1;
185                         parentStack [0] = nodeIndex;
186
187                         if (xmlReader.ReadState == ReadState.Initial)
188                                 xmlReader.Read ();
189                         int startDepth = xmlReader.Depth;
190                         do {
191                                 Read ();
192                         } while (skipRead || xmlReader.Read () && xmlReader.Depth >= startDepth);
193                         SetNodeArrayLength (nodeIndex + 1);
194                         SetAttributeArrayLength (attributeIndex + 1);
195                         SetNsArrayLength (nsIndex + 1);
196
197                         string [] newArr = new string [atomicIndex];
198                         Array.Copy (atomicStringPool, newArr, atomicIndex);
199                         atomicStringPool = newArr;
200
201                         newArr = new string [nonAtomicIndex];
202                         Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
203                         nonAtomicStringPool = newArr;
204
205                         xmlReader = null;       // It is no more required.
206                 }
207
208                 public void Read ()
209                 {
210                         skipRead = false;
211                         int parent = parentStack [parentStackIndex];
212                         int prevSibling = nodeIndex;
213
214                         switch (xmlReader.NodeType) {
215                         case XmlNodeType.Element:
216                         case XmlNodeType.CDATA:
217                         case XmlNodeType.Whitespace:
218                         case XmlNodeType.SignificantWhitespace:
219                         case XmlNodeType.Comment:
220                         case XmlNodeType.Text:
221                         case XmlNodeType.ProcessingInstruction:
222                                 break;
223                         case XmlNodeType.EndElement:
224                                 int endedNode = parentStack [parentStackIndex];
225                                 AdjustLastNsInScope (endedNode);
226                                 parentStackIndex--;
227                                 return;
228                         default:
229                                 // No operations. Doctype, EntityReference, 
230                                 return;
231                         }
232
233                         string value = null;
234                         XPathNodeType nodeType = XPathNodeType.Root; // dummy
235                         bool containsCDATA = false;
236
237                         switch (xmlReader.NodeType) {
238                         case XmlNodeType.Element:
239                                 nodeType = XPathNodeType.Element;
240                                 goto case XmlNodeType.None;
241                         case XmlNodeType.SignificantWhitespace:
242                         case XmlNodeType.Whitespace:
243                         case XmlNodeType.CDATA:
244                         case XmlNodeType.Text:
245                                 skipRead = true;
246                                 do {
247                                         switch (xmlReader.NodeType) {
248                                         case XmlNodeType.Whitespace:
249                                                 switch (nodeType) {
250                                                 case XPathNodeType.Root:
251                                                         nodeType = XPathNodeType.Whitespace;
252                                                         break;
253                                                 }
254                                                 // whitespaces after CDATA are ignored
255                                                 if (!containsCDATA)
256                                                         value += xmlReader.Value;
257                                                 continue;
258                                         case XmlNodeType.SignificantWhitespace:
259                                                 if (nodeType == XPathNodeType.Root ||
260                                                     nodeType == XPathNodeType.Whitespace)
261                                                         nodeType = XPathNodeType.SignificantWhitespace;
262                                                 value += xmlReader.Value;
263                                                 continue;
264                                         case XmlNodeType.CDATA:
265                                                 // whitespaces before CDATA are ignored
266                                                 if (nodeType != XPathNodeType.Text)
267                                                         value = String.Empty;
268                                                 containsCDATA = true;
269                                                 goto case XmlNodeType.Text;
270                                         case XmlNodeType.Text:
271                                                 nodeType = XPathNodeType.Text;
272                                                 value += xmlReader.Value;
273                                                 continue;
274                                         }
275                                         break;
276                                 } while (xmlReader.Read ());
277                                 goto case XmlNodeType.None;
278                         case XmlNodeType.None:
279                                 if (nodeType == XPathNodeType.Root ||
280                                     nodeType == XPathNodeType.Whitespace && xmlSpace != XmlSpace.Preserve)
281                                         return; // do not process as a node.
282
283                                 // prepare a slot for new node.
284                                 if (parent == nodeIndex)
285                                         prevSibling = 0;
286                                 else
287                                         while (nodes [prevSibling].Parent != parent)
288                                                 prevSibling = nodes [prevSibling].Parent;
289
290                                 nodeIndex++;
291
292                                 if (prevSibling != 0)
293                                         nodes [prevSibling].NextSibling = nodeIndex;
294                                 if (parentStack [parentStackIndex] == nodeIndex - 1)
295                                         nodes [parent].FirstChild = nodeIndex;
296
297                                 // append new node.
298                                 if (nodeType == XPathNodeType.Element) {
299                                         ProcessElement (parent, prevSibling);
300                                         break;
301                                 }
302                                 AddNode (parent,
303                                         0,
304                                         prevSibling,
305                                         nodeType,
306                                         AtomicIndex (xmlReader.BaseURI),
307                                         xmlReader.IsEmptyElement,
308                                         skipRead ? 0 : AtomicIndex (xmlReader.LocalName),       // for PI
309                                         skipRead ? 0 : AtomicIndex (xmlReader.NamespaceURI),    // for PI
310                                         AtomicIndex (xmlReader.Prefix),
311                                         value == null ? 0 : NonAtomicIndex (value),
312                                         AtomicIndex (xmlReader.XmlLang),
313                                         nsIndex,
314                                         lineInfo != null ? lineInfo.LineNumber : 0,
315                                         lineInfo != null ? lineInfo.LinePosition : 0);
316                                 break;
317                         case XmlNodeType.Comment:
318                                 value = xmlReader.Value;
319                                 nodeType = XPathNodeType.Comment;
320                                 goto case XmlNodeType.None;
321                         case XmlNodeType.ProcessingInstruction:
322                                 value = xmlReader.Value;
323                                 nodeType = XPathNodeType.ProcessingInstruction;
324                                 goto case XmlNodeType.None;
325                         }
326                 }
327
328                 private void ProcessElement (int parent, int previousSibling)
329                 {
330                         WriteStartElement (parent, previousSibling);
331
332                         // process namespaces and attributes.
333                         if (xmlReader.MoveToFirstAttribute ()) {
334                                 do {
335                                         string prefix = xmlReader.Prefix;
336                                         string ns = xmlReader.NamespaceURI;
337                                         if (ns == XmlNamespaces.XMLNS)
338                                                 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : xmlReader.LocalName, xmlReader.Value);
339                                         else
340                                                 ProcessAttribute (prefix, xmlReader.LocalName, ns, xmlReader.Value);
341
342                                 } while (xmlReader.MoveToNextAttribute ());
343                                 xmlReader.MoveToElement ();
344                         }
345
346                         CloseStartElement ();
347                 }
348
349                 private void PrepareStartElement (int previousSibling)
350                 {
351                         hasAttributes = false;
352                         hasLocalNs = false;
353                         attrIndexAtStart = attributeIndex;
354                         nsIndexAtStart = nsIndex;
355                         AdjustLastNsInScope (previousSibling);
356                 }
357
358                 private void AdjustLastNsInScope (int target)
359                 {
360                         while (namespaces [lastNsInScope].DeclaredElement == target) {
361                                 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
362                         }
363                 }
364
365                 private void WriteStartElement (int parent, int previousSibling)
366                 {
367                         PrepareStartElement (previousSibling);
368
369                         AddNode (parent,
370                                 0, // dummy:firstAttribute
371                                 previousSibling,
372                                 XPathNodeType.Element,
373                                 AtomicIndex (xmlReader.BaseURI),
374                                 xmlReader.IsEmptyElement,
375                                 AtomicIndex (xmlReader.LocalName),
376                                 AtomicIndex (xmlReader.NamespaceURI),
377                                 AtomicIndex (xmlReader.Prefix),
378                                 0,      // Element has no internal value.
379                                 AtomicIndex (xmlReader.XmlLang),
380                                 lastNsInScope,
381                                 lineInfo != null ? lineInfo.LineNumber : 0,
382                                 lineInfo != null ? lineInfo.LinePosition : 0);
383
384                 }
385
386                 private void CloseStartElement ()
387                 {
388                         if (attrIndexAtStart != attributeIndex)
389                                 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
390                         if (nsIndexAtStart != nsIndex) {
391                                 nodes [nodeIndex].FirstNamespace = nsIndex;
392                                 if (!xmlReader.IsEmptyElement)
393                                         lastNsInScope = nsIndex;
394                         }
395
396                         if (!nodes [nodeIndex].IsEmptyElement) {
397                                 parentStackIndex++;
398                                 if (parentStack.Length == parentStackIndex) {
399                                         int [] tmp = new int [parentStackIndex * 2];
400                                         Array.Copy (parentStack, tmp, parentStackIndex);
401                                         parentStack = tmp;
402                                 }
403                                 parentStack [parentStackIndex] = nodeIndex;
404                         }
405                 }
406
407                 private void ProcessNamespace (string prefix, string ns)
408                 {
409                         int nextTmp = hasLocalNs ?
410                                 nsIndex : nodes [nodeIndex].FirstNamespace;
411
412                         nsIndex++;
413
414                         this.AddNsNode (nodeIndex,
415                                 AtomicIndex (prefix),
416                                 AtomicIndex (ns),
417                                 nextTmp);
418                         hasLocalNs = true;
419                 }
420
421                 private void ProcessAttribute (string prefix, string localName, string ns, string value)
422                 {
423                         attributeIndex ++;
424
425                         this.AddAttribute (nodeIndex,
426                                 AtomicIndex (localName),
427                                 AtomicIndex (ns), 
428                                 prefix != null ? AtomicIndex (prefix) : 0, 
429                                 NonAtomicIndex (value),
430                                 lineInfo != null ? lineInfo.LineNumber : 0,
431                                 lineInfo != null ? lineInfo.LinePosition : 0);
432                         if (hasAttributes)
433                                 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
434                         else
435                                 hasAttributes = true;
436
437                         // Identity infoset
438                         if (validatingReader != null) {
439                                 XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
440                                 if (dt == null) {
441                                         XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
442                                         if (xsType != null)
443                                                 dt = xsType.Datatype;
444                                 }
445                                 if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
446                                         idTable.Add (value, nodeIndex);
447                         }
448                 }
449
450                 private int AtomicIndex (string s)
451                 {
452                         if (s == "")
453                                 return 0;
454                         if (s == null)
455                                 return 1;
456                         int i = 2;
457                         for (; i < atomicIndex; i++)
458                                 if (Object.ReferenceEquals (s, atomicStringPool [i]))
459                                         return i;
460
461                         if (atomicIndex == atomicStringPool.Length) {
462                                 string [] newArr = new string [atomicIndex * 2];
463                                 Array.Copy (atomicStringPool, newArr, atomicIndex);
464                                 atomicStringPool = newArr;
465                         }
466                         atomicStringPool [atomicIndex] = s;
467                         return atomicIndex++;
468                 }
469
470                 private int NonAtomicIndex (string s)
471                 {
472                         if (s == "")
473                                 return 0;
474                         if (s == null)
475                                 return 1;
476                         int i = 2;
477
478                         // Here we don't compare all the entries (sometimes it
479                         // goes extremely slow).
480                         int max = nonAtomicIndex < 100 ? nonAtomicIndex : 100;
481                         for (; i < max; i++)
482                                 if (s == nonAtomicStringPool [i])
483                                         return i;
484
485                         if (nonAtomicIndex == nonAtomicStringPool.Length) {
486                                 string [] newArr = new string [nonAtomicIndex * 2];
487                                 Array.Copy (nonAtomicStringPool, newArr, nonAtomicIndex);
488                                 nonAtomicStringPool = newArr;
489                         }
490                         nonAtomicStringPool [nonAtomicIndex] = s;
491                         return nonAtomicIndex++;
492                 }
493
494                 private void SetNodeArrayLength (int size)
495                 {
496                         DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
497                         Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
498                         nodes = newArr;
499                 }
500
501                 private void SetAttributeArrayLength (int size)
502                 {
503                         DTMXPathAttributeNode2 [] newArr = 
504                                 new DTMXPathAttributeNode2 [size];
505                         Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
506                         attributes = newArr;
507                 }
508
509                 private void SetNsArrayLength (int size)
510                 {
511                         DTMXPathNamespaceNode2 [] newArr =
512                                 new DTMXPathNamespaceNode2 [size];
513                         Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
514                         namespaces = newArr;
515                 }
516
517                 // Here followings are skipped: firstChild, nextSibling, 
518                 public void AddNode (int parent, int firstAttribute, int previousSibling, XPathNodeType nodeType, int baseUri, bool isEmptyElement, int localName, int ns, int prefix, int value, int xmlLang, int namespaceNode, int lineNumber, int linePosition)
519                 {
520                         if (nodes.Length < nodeIndex + 1) {
521                                 nodeCapacity *= 4;
522                                 SetNodeArrayLength (nodeCapacity);
523                         }
524
525 #if DTM_CLASS
526                         nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
527 #endif
528                         nodes [nodeIndex].FirstChild = 0;               // dummy
529                         nodes [nodeIndex].Parent = parent;
530                         nodes [nodeIndex].FirstAttribute = firstAttribute;
531                         nodes [nodeIndex].PreviousSibling = previousSibling;
532                         nodes [nodeIndex].NextSibling = 0;      // dummy
533                         nodes [nodeIndex].NodeType = nodeType;
534                         nodes [nodeIndex].BaseURI = baseUri;
535                         nodes [nodeIndex].IsEmptyElement = isEmptyElement;
536                         nodes [nodeIndex].LocalName = localName;
537                         nodes [nodeIndex].NamespaceURI = ns;
538                         nodes [nodeIndex].Prefix = prefix;
539                         nodes [nodeIndex].Value = value;
540                         nodes [nodeIndex].XmlLang = xmlLang;
541                         nodes [nodeIndex].FirstNamespace = namespaceNode;
542                         nodes [nodeIndex].LineNumber = lineNumber;
543                         nodes [nodeIndex].LinePosition = linePosition;
544                 }
545
546                 // Followings are skipped: nextAttribute,
547                 public void AddAttribute (int ownerElement, int localName, int ns, int prefix, int value, int lineNumber, int linePosition)
548                 {
549                         if (attributes.Length < attributeIndex + 1) {
550                                 attributeCapacity *= 4;
551                                 SetAttributeArrayLength (attributeCapacity);
552                         }
553
554 #if DTM_CLASS
555                         attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
556 #endif
557                         attributes [attributeIndex].OwnerElement = ownerElement;
558                         attributes [attributeIndex].LocalName = localName;
559                         attributes [attributeIndex].NamespaceURI = ns;
560                         attributes [attributeIndex].Prefix = prefix;
561                         attributes [attributeIndex].Value = value;
562                         attributes [attributeIndex].LineNumber = lineNumber;
563                         attributes [attributeIndex].LinePosition = linePosition;
564                 }
565
566                 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
567                 public void AddNsNode (int declaredElement, int name, int ns, int nextNs)
568                 {
569                         if (namespaces.Length < nsIndex + 1) {
570                                 nsCapacity *= 4;
571                                 SetNsArrayLength (nsCapacity);
572                         }
573
574 #if DTM_CLASS
575                         namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
576 #endif
577                         namespaces [nsIndex].DeclaredElement = declaredElement;
578                         namespaces [nsIndex].Name = name;
579                         namespaces [nsIndex].Namespace = ns;
580                         namespaces [nsIndex].NextNamespace = nextNs;
581                 }
582         }
583 }
584