2003-12-18 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, 400)
23                 {
24                 }
25
26                 public DTMXPathDocumentBuilder (string url, XmlSpace space)
27                         : this (url, space, 400)
28                 {
29                 }
30
31                 public DTMXPathDocumentBuilder (string url, XmlSpace space, int defaultCapacity)
32                         : this (new XmlTextReader (url), space, defaultCapacity)
33                 {
34                 }
35
36                 public DTMXPathDocumentBuilder (XmlReader reader)
37                         : this (reader, XmlSpace.None, 400)
38                 {
39                 }
40
41                 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space)
42                         : this (reader, space, 400)
43                 {
44                 }
45
46                 public DTMXPathDocumentBuilder (XmlReader reader, XmlSpace space, int defaultCapacity)
47                 {
48                         this.xmlReader = reader;
49                         this.validatingReader = reader as XmlValidatingReader;
50                         lineInfo = reader as IXmlLineInfo;
51                         this.xmlSpace = space;
52                         this.nameTable = reader.NameTable;
53                         nodeCapacity = nodeCapacity;
54                         attributeCapacity = nodeCapacity * 2;
55                         Compile ();
56                 }
57                 
58                 XmlReader xmlReader;
59                 XmlValidatingReader validatingReader;
60                 XmlSpace xmlSpace;
61                 XmlNameTable nameTable;
62                 IXmlLineInfo lineInfo;
63                 int nodeCapacity = 400;
64                 int attributeCapacity = 800;
65                 int nsCapacity = 10;
66
67                 // Linked Node
68                 DTMXPathLinkedNode [] nodes = new DTMXPathLinkedNode [0];
69
70                 // Attribute
71                 DTMXPathAttributeNode [] attributes = new DTMXPathAttributeNode [0];
72
73                 // NamespaceNode
74                 DTMXPathNamespaceNode [] namespaces = new DTMXPathNamespaceNode [0];
75
76                 // idTable [string value] -> int nodeId
77                 Hashtable idTable;
78
79                 int nodeIndex;
80                 int attributeIndex;
81                 int nsIndex;
82                 bool requireFirstChildFill;
83
84                 int prevSibling;
85                 int position;
86                 bool skipRead = false;
87
88                 public DTMXPathDocument CreateDocument ()
89                 {
90                         return new DTMXPathDocument (nameTable,
91                                 nodes,
92                                 attributes,
93                                 namespaces,
94                                 idTable
95                         );
96                 }
97
98                 public void Compile ()
99                 {
100                         idTable = new Hashtable ();
101
102                         // index 0 is dummy. No node (including Root) is assigned to this index
103                         // So that we can easily compare index != 0 instead of index < 0.
104                         // (Difference between jnz or jbe in 80x86.)
105                         AddNode (0, 0, 0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
106                         nodeIndex++;
107                         AddAttribute (0, null, null, null, null, null, 0, 0);
108 //                      attributes [0].NextAttribute = 0;
109                         AddNsNode (0, null, null);
110                         nsIndex++;
111 //                      nextNsNode_ [0] = 0;
112                         AddNsNode (1, "xml", XmlNamespaces.XML);
113 //                      nextNsNode_ [1] = 0;
114
115                         // add root.
116                         AddNode (0, 0, 0, 0, -1, 0, XPathNodeType.Root, xmlReader.BaseURI, false, "", "", "", "", "", 1, 0, 0);
117
118                         this.nodeIndex = 1;
119                         this.requireFirstChildFill = true;
120
121                         while (!xmlReader.EOF)
122                                 Read ();
123                         SetNodeArrayLength (nodeIndex + 1);
124                         SetAttributeArrayLength (attributeIndex + 1);
125                         SetNsArrayLength (nsIndex + 1);
126
127                         xmlReader = null;       // It is no more required.
128                 }
129
130                 public void Read ()
131                 {
132                         if (!skipRead)
133                                 if (!xmlReader.Read ())
134                                         return;
135                         skipRead = false;
136                         int parent = nodeIndex;
137
138                         if (nodes [nodeIndex].Depth >= xmlReader.Depth) {       // not ">=" ? But == worked when with ArrayList...
139                                 // if not, then current node is parent.
140                                 while (xmlReader.Depth <= nodes [parent].Depth)
141                                         parent = nodes [parent].Parent;
142                         }
143
144                         prevSibling = nodeIndex;
145                         position = 0;
146                         switch (xmlReader.NodeType) {
147                         case XmlNodeType.Element:
148                         case XmlNodeType.CDATA:
149                         case XmlNodeType.SignificantWhitespace:
150                         case XmlNodeType.Comment:
151                         case XmlNodeType.Text:
152                         case XmlNodeType.ProcessingInstruction:
153                                 if (requireFirstChildFill)
154                                         prevSibling = 0;
155                                 else
156                                         while (nodes [prevSibling].Depth != xmlReader.Depth)
157                                                 prevSibling = nodes [prevSibling].Parent;
158                                 if (prevSibling != 0)
159                                         position = nodes [prevSibling].Position + 1;
160
161                                 nodeIndex++;
162
163                                 if (prevSibling != 0)
164                                         nodes [prevSibling].NextSibling = nodeIndex;
165                                 if (requireFirstChildFill)
166                                         nodes [parent].FirstChild = nodeIndex;
167                                 break;
168                         case XmlNodeType.Whitespace:
169                                 if (xmlSpace == XmlSpace.Preserve)
170                                         goto case XmlNodeType.Text;
171                                 else
172                                         goto default;
173                         case XmlNodeType.EndElement:
174                                 requireFirstChildFill = false;
175                                 return;
176                         default:
177                                 // No operations. Doctype, EntityReference, 
178                                 return;
179                         }
180
181                         requireFirstChildFill = false;  // Might be changed in ProcessElement().
182
183                         string value = null;
184                         XPathNodeType nodeType = xmlReader.NodeType == XmlNodeType.Whitespace ?
185                                 XPathNodeType.Whitespace : XPathNodeType.Text;
186
187                         switch (xmlReader.NodeType) {
188                         case XmlNodeType.Element:
189                                 ProcessElement (parent, prevSibling, position);
190                                 break;
191                         case XmlNodeType.CDATA:
192                         case XmlNodeType.SignificantWhitespace:
193                         case XmlNodeType.Text:
194                         case XmlNodeType.Whitespace:
195                                 if (value == null)
196                                         skipRead = true;
197                                 AddNode (parent,
198                                         0,
199                                         attributeIndex,
200                                         prevSibling,
201                                         xmlReader.Depth,
202                                         position,
203                                         nodeType,
204                                         xmlReader.BaseURI,
205                                         xmlReader.IsEmptyElement,
206                                         xmlReader.LocalName,    // for PI
207                                         xmlReader.NamespaceURI, // for PI
208                                         xmlReader.Prefix,
209                                         value,
210                                         xmlReader.XmlLang,
211                                         nsIndex,
212                                         lineInfo != null ? lineInfo.LineNumber : 0,
213                                         lineInfo != null ? lineInfo.LinePosition : 0);
214                                 // this code is tricky, but after ReadString() invokation,
215                                 // xmlReader is moved to next node!!
216                                 if (value == null)
217                                         nodes [nodeIndex].Value = xmlReader.ReadString ();
218                                 break;
219                         case XmlNodeType.Comment:
220                                 value = xmlReader.Value;
221                                 nodeType = XPathNodeType.Comment;
222                                 goto case XmlNodeType.Text;
223                         case XmlNodeType.ProcessingInstruction:
224                                 value = xmlReader.Value;
225                                 nodeType = XPathNodeType.ProcessingInstruction;
226                                 goto case XmlNodeType.Text;
227                         }
228                 }
229
230                 private void ProcessElement (int parent, int previousSibling, int position)
231                 {
232                         int firstAttributeIndex = 0;
233                         int lastNsIndexInCurrent = 0;
234
235
236                         // process namespaces and attributes.
237                         if (xmlReader.MoveToFirstAttribute ()) {
238                                 do {
239                                         if (xmlReader.NamespaceURI == XmlNamespaces.XMLNS) {
240                                                 // add namespace node.
241                                                 nsIndex++;
242
243                                                 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [parent].FirstNamespace : lastNsIndexInCurrent;
244
245                                                 this.AddNsNode (nodeIndex,
246                                                         (xmlReader.Prefix == null || xmlReader.Prefix == String.Empty) ?
247                                                                 "" : xmlReader.LocalName,
248                                                         xmlReader.Value);
249                                                 namespaces [nsIndex].NextNamespace = nextTmp;
250 //                                              if (lastNsIndexInCurrent == 0)
251 //                                                      nodes [nodeIndex].FirstNamespace = nsIndex;
252                                                 lastNsIndexInCurrent = nsIndex;
253                                         } else {
254                                                 // add attribute node.
255                                                 attributeIndex ++;
256                                                 this.AddAttribute (nodeIndex,
257                                                         xmlReader.LocalName,
258                                                         xmlReader.NamespaceURI, 
259                                                         xmlReader.Prefix != null ? xmlReader.Prefix : String.Empty, 
260                                                         xmlReader.Value,
261                                                         null, 
262                                                         lineInfo != null ? lineInfo.LineNumber : 0,
263                                                         lineInfo != null ? lineInfo.LinePosition : 0);
264                                                 if (firstAttributeIndex == 0)
265                                                         firstAttributeIndex = attributeIndex;
266                                                 else
267                                                         attributes [attributeIndex - 1].NextAttribute = attributeIndex;
268                                                 // dummy for "current" attribute.
269                                                 attributes [attributeIndex].NextAttribute = 0;
270
271                                                 // Identity infoset
272                                                 if (validatingReader != null) {
273                                                         XmlSchemaDatatype dt = validatingReader.SchemaType as XmlSchemaDatatype;
274                                                         if (dt == null) {
275                                                                 XmlSchemaType xsType = validatingReader.SchemaType as XmlSchemaType;
276                                                                 if (xsType != null)
277                                                                         dt = xsType.Datatype;
278                                                         }
279                                                         if (dt != null && dt.TokenizedType == XmlTokenizedType.ID)
280                                                                 idTable.Add (xmlReader.Value, nodeIndex);
281                                                 }
282                                         }
283                                 } while (xmlReader.MoveToNextAttribute ());
284                                 xmlReader.MoveToElement ();
285                         }
286
287                         AddNode (parent,
288                                 firstAttributeIndex,
289                                 attributeIndex,
290                                 previousSibling,
291                                 xmlReader.Depth,
292                                 position,
293                                 XPathNodeType.Element,
294                                 xmlReader.BaseURI,
295                                 xmlReader.IsEmptyElement,
296                                 xmlReader.LocalName,
297                                 xmlReader.NamespaceURI,
298                                 xmlReader.Prefix,
299                                 "",     // Element has no internal value.
300                                 xmlReader.XmlLang,
301                                 nsIndex,
302                                 lineInfo != null ? lineInfo.LineNumber : 0,
303                                 lineInfo != null ? lineInfo.LinePosition : 0);
304                         if (!xmlReader.IsEmptyElement)
305                                 requireFirstChildFill = true;
306                 }
307
308                 private void SetNodeArrayLength (int size)
309                 {
310                         DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
311                         Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
312                         nodes = newArr;
313                 }
314
315                 private void SetAttributeArrayLength (int size)
316                 {
317                         DTMXPathAttributeNode [] newArr = 
318                                 new DTMXPathAttributeNode [size];
319                         Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
320                         attributes = newArr;
321                 }
322
323                 private void SetNsArrayLength (int size)
324                 {
325                         DTMXPathNamespaceNode [] newArr =
326                                 new DTMXPathNamespaceNode [size];
327                         Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
328                         namespaces = newArr;
329                 }
330
331                 // Here followings are skipped: firstChild, nextSibling, 
332                 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)
333                 {
334                         if (nodes.Length < nodeIndex + 1) {
335 //                              if (nodes.Length >= nodeCapacity) {
336                                         nodeCapacity *= 2;
337                                         SetNodeArrayLength (nodeCapacity);
338 //                              }
339                         }
340
341                         DTMXPathLinkedNode curNode = nodes [nodeIndex];// = new DTMXPathLinkedNode ();
342                         nodes [nodeIndex].FirstChild = 0;               // dummy
343                         nodes [nodeIndex].Parent = parent;
344                         nodes [nodeIndex].FirstAttribute = firstAttribute;
345                         nodes [nodeIndex].PreviousSibling = previousSibling;
346                         nodes [nodeIndex].NextSibling = 0;      // dummy
347                         nodes [nodeIndex].Depth = depth;
348                         nodes [nodeIndex].Position = position;
349                         nodes [nodeIndex].NodeType = nodeType;
350                         nodes [nodeIndex].BaseURI = baseUri;
351                         nodes [nodeIndex].IsEmptyElement = isEmptyElement;
352                         nodes [nodeIndex].LocalName = localName;
353                         nodes [nodeIndex].NamespaceURI = ns;
354                         nodes [nodeIndex].Prefix = prefix;
355                         nodes [nodeIndex].Value = value;
356                         nodes [nodeIndex].XmlLang = xmlLang;
357                         nodes [nodeIndex].FirstNamespace = namespaceNode;
358                         nodes [nodeIndex].LineNumber = lineNumber;
359                         nodes [nodeIndex].LinePosition = linePosition;
360                 }
361
362                 // Followings are skipped: nextAttribute,
363                 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
364                 {
365                         if (attributes.Length < attributeIndex + 1) {
366 //                              if (attributes.Length >= attributeCapacity) {
367                                         attributeCapacity *= 2;
368                                         SetAttributeArrayLength (attributeCapacity);
369 //                              }
370                         }
371
372                         DTMXPathAttributeNode attr = attributes [attributeIndex];// = new DTMXPathAttributeNode ();
373                         attributes [attributeIndex].OwnerElement = ownerElement;
374                         attributes [attributeIndex].LocalName = localName;
375                         attributes [attributeIndex].NamespaceURI = ns;
376                         attributes [attributeIndex].Prefix = prefix;
377                         attributes [attributeIndex].Value = value;
378                         attributes [attributeIndex].SchemaType = schemaType;
379                         attributes [attributeIndex].LineNumber = lineNumber;
380                         attributes [attributeIndex].LinePosition = linePosition;
381                 }
382
383                 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
384                 public void AddNsNode (int declaredElement, string name, string ns)
385                 {
386                         if (namespaces.Length < nsIndex + 1) {
387 //                              if (namespaces.Length >= nsCapacity) {
388                                         nsCapacity *= 2;
389                                         SetNsArrayLength (nsCapacity);
390 //                              }
391                         }
392
393                         DTMXPathNamespaceNode nsNode = namespaces [nsIndex];// = new DTMXPathNamespaceNode ();
394                         namespaces [nsIndex].DeclaredElement = declaredElement;
395                         namespaces [nsIndex].Name = name;
396                         namespaces [nsIndex].Namespace = ns;
397                 }
398         }
399 }
400