2003-06-01 Joshua Tauberer <tauberer@for.net>
[mono.git] / mcs / class / System.XML / System.Xml.Xsl / XslTransform.cs
1 // System.Xml.Xsl.XslTransform\r
2 //\r
3 // Authors:\r
4 //      Tim Coleman <tim@timcoleman.com>\r
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
6 //\r
7 // (C) Copyright 2002 Tim Coleman\r
8 // (c) 2003 Ximian Inc. (http://www.ximian.com)\r
9 //\r
10 \r
11 using System;\r
12 using System.Collections;\r
13 using System.IO;\r
14 using System.Text;\r
15 using System.Runtime.InteropServices;\r
16 using System.Xml.XPath;\r
17 \r
18 using BF = System.Reflection.BindingFlags;\r
19 \r
20 namespace System.Xml.Xsl\r
21 {\r
22         public unsafe sealed class XslTransform\r
23         {\r
24 \r
25                 #region Fields\r
26 \r
27                 XmlResolver xmlResolver;\r
28                 IntPtr stylesheet;\r
29                 Hashtable extensionObjectCache = new Hashtable();\r
30 \r
31                 #endregion\r
32 \r
33                 #region Constructors\r
34                 public XslTransform ()\r
35                 {\r
36                         stylesheet = IntPtr.Zero;\r
37                 }\r
38 \r
39                 #endregion\r
40 \r
41                 #region Properties\r
42 \r
43                 public XmlResolver XmlResolver {\r
44                         set { xmlResolver = value; }\r
45                 }\r
46 \r
47                 #endregion\r
48 \r
49                 #region Methods\r
50 \r
51                 ~XslTransform ()\r
52                 {\r
53                         FreeStylesheetIfNeeded ();\r
54                 }\r
55 \r
56                 void FreeStylesheetIfNeeded ()\r
57                 {\r
58                         if (stylesheet != IntPtr.Zero) {\r
59                                 xsltFreeStylesheet (stylesheet);\r
60                                 stylesheet = IntPtr.Zero;\r
61                         }\r
62                 }\r
63                 \r
64                 // Loads the XSLT stylesheet contained in the IXPathNavigable.\r
65                 public void Load (IXPathNavigable stylesheet)\r
66                 {\r
67                         Load (stylesheet.CreateNavigator ());\r
68                 }\r
69 \r
70                 // Loads the XSLT stylesheet specified by a URL.\r
71                 public void Load (string url)\r
72                 {\r
73                         if (url == null)\r
74                                 throw new ArgumentNullException ("url");\r
75 \r
76                         FreeStylesheetIfNeeded ();\r
77                         stylesheet = xsltParseStylesheetFile (url);\r
78                         Cleanup ();\r
79                         if (stylesheet == IntPtr.Zero)\r
80                                 throw new XmlException ("Error creating stylesheet");\r
81                 }\r
82 \r
83                 static IntPtr GetStylesheetFromString (string xml)\r
84                 {\r
85                         IntPtr result = IntPtr.Zero;\r
86 \r
87                         IntPtr xmlDoc = xmlParseDoc (xml);\r
88 \r
89                         if (xmlDoc == IntPtr.Zero) {\r
90                                 Cleanup ();\r
91                                 throw new XmlException ("Error parsing stylesheet");\r
92                         }\r
93                                 \r
94                         result = xsltParseStylesheetDoc (xmlDoc);\r
95                         Cleanup ();\r
96                         if (result == IntPtr.Zero)\r
97                                 throw new XmlException ("Error creating stylesheet");\r
98 \r
99                         return result;\r
100                 }\r
101 \r
102                 // Loads the XSLT stylesheet contained in the XmlReader\r
103                 public void Load (XmlReader stylesheet)\r
104                 {\r
105                         FreeStylesheetIfNeeded ();\r
106                         // Create a document for the stylesheet\r
107                         XmlDocument doc = new XmlDocument ();\r
108                         doc.Load (stylesheet);\r
109                         \r
110                         // Store the XML in a StringBuilder\r
111                         StringWriter sr = new UTF8StringWriter ();\r
112                         XmlTextWriter writer = new XmlTextWriter (sr);\r
113                         doc.Save (writer);\r
114 \r
115                         this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());\r
116                         Cleanup ();\r
117                         if (this.stylesheet == IntPtr.Zero)\r
118                                 throw new XmlException ("Error creating stylesheet");\r
119                 }\r
120 \r
121                 // Loads the XSLT stylesheet contained in the XPathNavigator\r
122                 public void Load (XPathNavigator stylesheet)\r
123                 {\r
124                         FreeStylesheetIfNeeded ();\r
125                         StringWriter sr = new UTF8StringWriter ();\r
126                         Save (stylesheet, sr);\r
127                         this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());\r
128                         Cleanup ();\r
129                         if (this.stylesheet == IntPtr.Zero)\r
130                                 throw new XmlException ("Error creating stylesheet");\r
131                 }\r
132 \r
133                 [MonoTODO("use the resolver")]\r
134                 // Loads the XSLT stylesheet contained in the IXPathNavigable.\r
135                 public void Load (IXPathNavigable stylesheet, XmlResolver resolver)\r
136                 {\r
137                         Load (stylesheet);\r
138                 }\r
139 \r
140                 [MonoTODO("use the resolver")]\r
141                 // Loads the XSLT stylesheet specified by a URL.\r
142                 public void Load (string url, XmlResolver resolver)\r
143                 {\r
144                         Load (url);\r
145                 }\r
146 \r
147                 [MonoTODO("use the resolver")]\r
148                 // Loads the XSLT stylesheet contained in the XmlReader\r
149                 public void Load (XmlReader stylesheet, XmlResolver resolver)\r
150                 {\r
151                         Load (stylesheet);\r
152                 }\r
153 \r
154                 [MonoTODO("use the resolver")]\r
155                 // Loads the XSLT stylesheet contained in the XPathNavigator\r
156                 public void Load (XPathNavigator stylesheet, XmlResolver resolver)\r
157                 {\r
158                         Load (stylesheet);\r
159                 }\r
160 \r
161                 // Transforms the XML data in the IXPathNavigable using\r
162                 // the specified args and outputs the result to an XmlReader.\r
163                 public XmlReader Transform (IXPathNavigable input, XsltArgumentList args)\r
164                 {\r
165                         if (input == null)\r
166                                 throw new ArgumentNullException ("input");\r
167 \r
168                         return Transform (input.CreateNavigator (), args);\r
169                 }\r
170 \r
171                 // Transforms the XML data in the input file and outputs\r
172                 // the result to an output file.\r
173                 public void Transform (string inputfile, string outputfile)\r
174                 {\r
175                         IntPtr xmlDocument = IntPtr.Zero;\r
176                         IntPtr resultDocument = IntPtr.Zero;\r
177 \r
178                         try {\r
179                                 xmlDocument = xmlParseFile (inputfile);\r
180                                 if (xmlDocument == IntPtr.Zero)\r
181                                         throw new XmlException ("Error parsing input file");\r
182 \r
183                                 resultDocument = ApplyStylesheet (xmlDocument, null, null);\r
184 \r
185                                 /*\r
186                                 * If I do this, the <?xml version=... is always present *\r
187                                 if (-1 == xsltSaveResultToFilename (outputfile, resultDocument, stylesheet, 0))\r
188                                         throw new XmlException ("Error xsltSaveResultToFilename");\r
189                                 */\r
190                                 StreamWriter writer = new StreamWriter (File.OpenWrite (outputfile));\r
191                                 writer.Write (GetStringFromDocument (resultDocument));\r
192                                 writer.Close ();\r
193                         } finally {\r
194                                 if (xmlDocument != IntPtr.Zero)\r
195                                         xmlFreeDoc (xmlDocument);\r
196 \r
197                                 if (resultDocument != IntPtr.Zero)\r
198                                         xmlFreeDoc (resultDocument);\r
199 \r
200                                 Cleanup ();\r
201                         }\r
202                 }\r
203 \r
204                 IntPtr ApplyStylesheet (IntPtr doc, string[] argArr, Hashtable extobjects)\r
205                 {\r
206                         if (stylesheet == IntPtr.Zero)\r
207                                 throw new XmlException ("No style sheet!");\r
208 \r
209                         IntPtr result;\r
210 \r
211                         if (extobjects == null || extobjects.Count == 0) {\r
212                                 // If there are no extension objects, use the simple (old) method.\r
213                                 result = xsltApplyStylesheet (stylesheet, doc, argArr);\r
214                         } else {\r
215                                 // If there are extension objects, create a context and register the functions.\r
216 \r
217                                 IntPtr context = xsltNewTransformContext(stylesheet, doc);\r
218 \r
219                                 if (context == IntPtr.Zero) throw new XmlException("Error creating transformation context.");\r
220 \r
221                                 try {\r
222                                         foreach (string ns in extobjects.Keys) {\r
223                                                 object ext = extobjects[ns];\r
224 \r
225                                                 if (extensionObjectCache.ContainsKey(ext)) {\r
226                                                         foreach (ExtensionFunctionHolder ef in (ArrayList)extensionObjectCache[ext]) {\r
227                                                                 int ret = xsltRegisterExtFunction(context, ef.name, ef.ns, ef.func);\r
228                                                                 if (ret != 0) throw new XmlException("Could not reregister extension function " + ef.name + " in " + ef.ns);\r
229                                                         }\r
230 \r
231                                                 } else {\r
232                                                         object extsrc;\r
233         \r
234                                                         System.Type type;\r
235                                                         System.Collections.IEnumerable methods;\r
236         \r
237                                                         // As an added bonus, if the extension object is a UseStaticMethods object\r
238                                                         // (defined below), then add the static methods of the specified type.\r
239                                                         if (ext is UseStaticMethods) {\r
240                                                                 type = ((UseStaticMethods)ext).Type;\r
241                                                                 methods = type.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);\r
242                                                                 extsrc = null;\r
243                                                         } else {\r
244                                                                 extsrc = ext;\r
245                                                                 type = ext.GetType();\r
246                                                                 methods = type.GetMethods();\r
247                                                         }\r
248 \r
249                                                         ArrayList functionstocache = new ArrayList();\r
250         \r
251                                                         Hashtable alreadyadded = new Hashtable ();\r
252                                                         foreach (System.Reflection.MethodInfo mi in methods) {\r
253                                                                 if (alreadyadded.ContainsKey(mi.Name)) continue; // don't add twice\r
254                                                                 alreadyadded[mi.Name] = 1;\r
255 \r
256                                                                 // Simple extension function delegate\r
257                                                                 ExtensionFunction func = new ExtensionFunction(new ReflectedExtensionFunction(type, extsrc, mi.Name).Function);\r
258         \r
259                                                                 // Delegate for libxslt library call\r
260                                                                 libxsltXPathFunction libfunc = new libxsltXPathFunction(new ExtensionFunctionWrapper(func).Function);\r
261                 \r
262                                                                 int ret = xsltRegisterExtFunction(context, mi.Name, ns, libfunc);\r
263                                                                 if (ret != 0) throw new XmlException("Could not register extension function " + mi.DeclaringType.FullName + "." + mi.Name + " in " + ns);\r
264 \r
265                                                                 ExtensionFunctionHolder efh;\r
266                                                                 efh.name = mi.Name;\r
267                                                                 efh.ns = ns;\r
268                                                                 efh.func = libfunc;\r
269                                                                 functionstocache.Add(efh);\r
270                                                         }\r
271 \r
272                                                         extensionObjectCache[ext] = functionstocache;\r
273 \r
274                                                 }\r
275                                                 \r
276                                         }\r
277         \r
278                                         result = xsltApplyStylesheetUser(stylesheet, doc, argArr, null, IntPtr.Zero, context);\r
279                                 } finally {\r
280                                         xsltFreeTransformContext(context);\r
281                                 }\r
282                         }\r
283 \r
284 \r
285                         if (result == IntPtr.Zero)\r
286                                 throw new XmlException ("Error applying style sheet");\r
287 \r
288                         return result;\r
289                 }\r
290 \r
291                 static void Cleanup ()\r
292                 {\r
293                         xsltCleanupGlobals ();\r
294                         xmlCleanupParser ();\r
295                 }\r
296 \r
297                 static string GetStringFromDocument (IntPtr doc)\r
298                 {\r
299                         IntPtr mem = IntPtr.Zero;\r
300                         int size = 0;\r
301                         xmlDocDumpMemory (doc, ref mem, ref size);\r
302                         if (mem == IntPtr.Zero)\r
303                                 throw new XmlException ("Error dumping document");\r
304 \r
305                         string docStr = Marshal.PtrToStringAnsi (mem, size);\r
306                         // FIXME: Using xmlFree segfaults :-???\r
307                         //xmlFree (mem);\r
308                         Marshal.FreeHGlobal (mem);\r
309                         //\r
310 \r
311                         // Get rid of the <?xml...\r
312                         // FIXME: any other (faster) way that works?\r
313                         StringReader result = new StringReader (docStr);\r
314                         result.ReadLine (); // we want the semantics of line ending used here\r
315                         //\r
316                         return result.ReadToEnd ();\r
317                 }\r
318 \r
319                 string ApplyStylesheetAndGetString (IntPtr doc, string[] argArr, Hashtable extobjects)\r
320                 {\r
321                         IntPtr xmlOutput = ApplyStylesheet (doc, argArr, extobjects);\r
322                         string strOutput = GetStringFromDocument (xmlOutput);\r
323                         xmlFreeDoc (xmlOutput);\r
324 \r
325                         return strOutput;\r
326                 }\r
327 \r
328                 IntPtr GetDocumentFromNavigator (XPathNavigator nav)\r
329                 {\r
330                         StringWriter sr = new UTF8StringWriter ();\r
331                         Save (nav, sr);\r
332                         IntPtr xmlInput = xmlParseDoc (sr.GetStringBuilder ().ToString ());\r
333                         if (xmlInput == IntPtr.Zero)\r
334                                 throw new XmlException ("Error getting XML from input");\r
335 \r
336                         return xmlInput;\r
337                 }\r
338 \r
339                 [MonoTODO("Node Set and Node Fragment Parameters and Extension Objects")]\r
340                 // Transforms the XML data in the XPathNavigator using\r
341                 // the specified args and outputs the result to an XmlReader.\r
342                 public XmlReader Transform (XPathNavigator input, XsltArgumentList args)\r
343                 {\r
344                         IntPtr xmlInput = GetDocumentFromNavigator (input);\r
345                         string[] argArr = null;\r
346                         Hashtable extensionObjects = null;\r
347                         if (args != null) {\r
348                                 extensionObjects = args.extensionObjects;\r
349                                 argArr = new string[args.parameters.Count * 2 + 1];\r
350                                 int index = 0;\r
351                                 foreach (object key in args.parameters.Keys) {\r
352                                         argArr [index++] = key.ToString();\r
353                                         object value = args.parameters [key];\r
354                                         if (value is Boolean)\r
355                                                 argArr [index++] = XmlConvert.ToString((bool) value); // FIXME: How to encode it for libxslt?\r
356                                         else if (value is Double)\r
357                                                 argArr [index++] = XmlConvert.ToString((double) value); // FIXME: How to encode infinity's and Nan?\r
358                                         else\r
359                                                 argArr [index++] = "'" + value.ToString() + "'"; // FIXME: How to encode "'"?\r
360                                 }\r
361                                 argArr[index] = null;\r
362                         }\r
363                         string xslOutputString = ApplyStylesheetAndGetString (xmlInput, argArr, extensionObjects);\r
364                         xmlFreeDoc (xmlInput);\r
365                         Cleanup ();\r
366 \r
367                         return new XmlTextReader (new StringReader (xslOutputString));\r
368                 }\r
369 \r
370                 // Transforms the XML data in the IXPathNavigable using\r
371                 // the specified args and outputs the result to a Stream.\r
372                 public void Transform (IXPathNavigable input, XsltArgumentList args, Stream output)\r
373                 {\r
374                         if (input == null)\r
375                                 throw new ArgumentNullException ("input");\r
376 \r
377                         Transform (input.CreateNavigator (), args, new StreamWriter (output));\r
378                 }\r
379 \r
380                 // Transforms the XML data in the IXPathNavigable using\r
381                 // the specified args and outputs the result to a TextWriter.\r
382                 public void Transform (IXPathNavigable input, XsltArgumentList args, TextWriter output)\r
383                 {\r
384                         if (input == null)\r
385                                 throw new ArgumentNullException ("input");\r
386 \r
387                         Transform (input.CreateNavigator (), args, output);\r
388                 }\r
389 \r
390                 // Transforms the XML data in the IXPathNavigable using\r
391                 // the specified args and outputs the result to an XmlWriter.\r
392                 public void Transform (IXPathNavigable input, XsltArgumentList args, XmlWriter output)\r
393                 {\r
394                         if (input == null)\r
395                                 throw new ArgumentNullException ("input");\r
396 \r
397                         Transform (input.CreateNavigator (), args, output);\r
398                 }\r
399 \r
400                 // Transforms the XML data in the XPathNavigator using\r
401                 // the specified args and outputs the result to a Stream.\r
402                 public void Transform (XPathNavigator input, XsltArgumentList args, Stream output)\r
403                 {\r
404                         Transform (input, args, new StreamWriter (output));\r
405                 }\r
406 \r
407                 // Transforms the XML data in the XPathNavigator using\r
408                 // the specified args and outputs the result to a TextWriter.\r
409                 [MonoTODO("Node Set and Node Fragment Parameters and Extension Objects")]\r
410                 public void Transform (XPathNavigator input, XsltArgumentList args, TextWriter output)\r
411                 {\r
412                         if (input == null)\r
413                                 throw new ArgumentNullException ("input");\r
414 \r
415                         if (output == null)\r
416                                 throw new ArgumentNullException ("output");\r
417 \r
418                         IntPtr inputDoc = GetDocumentFromNavigator (input);\r
419                         string[] argArr = null;\r
420                         Hashtable extensionObjects = null;\r
421                         if (args != null) {\r
422                                 extensionObjects = args.extensionObjects;\r
423                                 argArr = new string[args.parameters.Count * 2 + 1];\r
424                                 int index = 0;\r
425                                 foreach (object key in args.parameters.Keys) {\r
426                                         argArr [index++] = key.ToString();\r
427                                         object value = args.parameters [key];\r
428                                         if (value is Boolean)\r
429                                                 argArr [index++] = XmlConvert.ToString((bool) value); // FIXME: How to encode it for libxslt?\r
430                                         else if (value is Double)\r
431                                                 argArr [index++] = XmlConvert.ToString((double) value); // FIXME: How to encode infinity's and Nan?\r
432                                         else\r
433                                                 argArr [index++] = "'" + value.ToString() + "'"; // FIXME: How to encode "'"?\r
434                                 }\r
435                                 argArr[index] = null;\r
436                         }\r
437                         string transform = ApplyStylesheetAndGetString (inputDoc, argArr, extensionObjects);\r
438                         xmlFreeDoc (inputDoc);\r
439                         Cleanup ();\r
440                         output.Write (transform);\r
441                         output.Flush ();\r
442                 }\r
443 \r
444                 // Transforms the XML data in the XPathNavigator using\r
445                 // the specified args and outputs the result to an XmlWriter.\r
446                 public void Transform (XPathNavigator input, XsltArgumentList args, XmlWriter output)\r
447                 {\r
448                         StringWriter writer = new UTF8StringWriter ();\r
449                         Transform (input, args, writer);\r
450                         output.WriteRaw (writer.GetStringBuilder ().ToString ());\r
451                         output.Flush ();\r
452                 }\r
453 \r
454                 static void Save (XmlReader rdr, TextWriter baseWriter)\r
455                 {\r
456                         XmlTextWriter writer = new XmlTextWriter (baseWriter);\r
457                 \r
458                         while (rdr.Read ()) {\r
459                                 switch (rdr.NodeType) {\r
460                                 \r
461                                 case XmlNodeType.CDATA:\r
462                                         writer.WriteCData (rdr.Value);\r
463                                         break;\r
464                                 \r
465                                 case XmlNodeType.Comment:\r
466                                         writer.WriteComment (rdr.Value);\r
467                                         break;\r
468 \r
469                                 case XmlNodeType.DocumentType:\r
470                                         writer.WriteDocType (rdr.Value, null, null, null);\r
471                                         break;\r
472 \r
473                                 case XmlNodeType.Element:\r
474                                         writer.WriteStartElement (rdr.Name, rdr.Value);\r
475                                 \r
476                                         while (rdr.MoveToNextAttribute ())\r
477                                                 writer.WriteAttributes (rdr, true);\r
478                                         break;\r
479                         \r
480                                 case XmlNodeType.EndElement:\r
481                                         writer.WriteEndElement ();\r
482                                         break;\r
483 \r
484                                 case XmlNodeType.ProcessingInstruction:\r
485                                         writer.WriteProcessingInstruction (rdr.Name, rdr.Value);\r
486                                         break;\r
487 \r
488                                 case XmlNodeType.Text:\r
489                                         writer.WriteString (rdr.Value);\r
490                                         break;\r
491 \r
492                                 case XmlNodeType.Whitespace:\r
493                                         writer.WriteWhitespace (rdr.Value);\r
494                                         break;\r
495 \r
496                                 case XmlNodeType.XmlDeclaration:\r
497                                         writer.WriteStartDocument ();\r
498                                         break;\r
499                                 }\r
500                         }\r
501 \r
502                         writer.Close ();\r
503                 }\r
504 \r
505                 static void Save (XPathNavigator navigator, TextWriter writer)\r
506                 {\r
507                         XmlTextWriter xmlWriter = new XmlTextWriter (writer);\r
508 \r
509                         WriteTree (navigator, xmlWriter);\r
510                         xmlWriter.WriteEndDocument ();\r
511                         xmlWriter.Flush ();\r
512                 }\r
513 \r
514                 // Walks the XPathNavigator tree recursively \r
515                 static void WriteTree (XPathNavigator navigator, XmlTextWriter writer)\r
516                 {\r
517                         WriteCurrentNode (navigator, writer);\r
518 \r
519                         if (navigator.MoveToFirstAttribute ()) {\r
520                                 do {\r
521                                         WriteCurrentNode (navigator, writer);\r
522                                 } while (navigator.MoveToNextAttribute ());\r
523 \r
524                                 navigator.MoveToParent ();\r
525                         }\r
526 \r
527                         if (navigator.MoveToFirstChild ()) {\r
528                                 do {\r
529                                         WriteTree (navigator, writer);\r
530                                 } while (navigator.MoveToNext ());\r
531 \r
532                                 navigator.MoveToParent ();\r
533                                 if (navigator.NodeType != XPathNodeType.Root)\r
534                                         writer.WriteEndElement ();\r
535                         } else if (navigator.NodeType == XPathNodeType.Element) {\r
536                                 writer.WriteEndElement ();\r
537                         }\r
538                 }\r
539 \r
540                 // Format the output  \r
541                 static void WriteCurrentNode (XPathNavigator navigator, XmlTextWriter writer)\r
542                 {\r
543                         switch (navigator.NodeType) {\r
544                         case XPathNodeType.Root:\r
545                                 writer.WriteStartDocument ();\r
546                                 break;\r
547                         case XPathNodeType.Attribute:\r
548                                 writer.WriteAttributeString (navigator.Name, navigator.Value);\r
549                                 break;\r
550 \r
551                         case XPathNodeType.Comment:\r
552                                 writer.WriteComment (navigator.Value);\r
553                                 break;\r
554 \r
555                         case XPathNodeType.Element:\r
556                                 writer.WriteStartElement (navigator.Name);\r
557                                 break;\r
558                         \r
559                         case XPathNodeType.ProcessingInstruction:\r
560                                 writer.WriteProcessingInstruction (navigator.Name, navigator.Value);\r
561                                 break;\r
562 \r
563                         case XPathNodeType.Text:\r
564                                 writer.WriteString (navigator.Value);\r
565                                 break;\r
566 \r
567                         case XPathNodeType.SignificantWhitespace:\r
568                         case XPathNodeType.Whitespace:\r
569                                 writer.WriteWhitespace (navigator.Value);\r
570                                 break;\r
571                         }\r
572                 }\r
573 \r
574                 // Extension Objects\r
575 \r
576                 internal delegate object ExtensionFunction(object[] args);\r
577 \r
578                 private struct ExtensionFunctionHolder {\r
579                         public libxsltXPathFunction func;\r
580                         public string ns, name;\r
581                 }\r
582 \r
583                 // Wraps an ExtensionFunction into a function that is callable from the libxslt library.\r
584                 private unsafe class ExtensionFunctionWrapper {\r
585                         private readonly ExtensionFunction func;\r
586 \r
587                         public ExtensionFunctionWrapper(ExtensionFunction func) {\r
588                                 if ((object)func == null) throw new ArgumentNullException("func");\r
589                                 this.func = func;\r
590                         }\r
591 \r
592                         public unsafe void Function(IntPtr xpath_ctxt, int nargs) {\r
593                                 // Convert XPath arguments into "managed" arguments\r
594                                 System.Collections.ArrayList args = new System.Collections.ArrayList();\r
595                                 for (int i = 0; i < nargs; i++) {\r
596                                         xpathobject* aptr = valuePop(xpath_ctxt);\r
597                                         if (aptr->type == 2) // Booleans\r
598                                                 args.Add( xmlXPathCastToBoolean(aptr) == 0 ? false : true );\r
599                                         else if (aptr->type == 3) // Doubles\r
600                                                 args.Add( xmlXPathCastToNumber(aptr));\r
601                                         else if (aptr->type == 4) // Strings\r
602                                                 args.Add( xmlXPathCastToString(aptr));\r
603                                         else if (aptr->type == 1 && aptr->nodesetptr != null) { // Node Sets ==> ArrayList of strings\r
604                                                 System.Collections.ArrayList a = new System.Collections.ArrayList();\r
605                                                 for (int ni = 0; ni < aptr->nodesetptr->count; ni++) {\r
606                                                         xpathobject *n = xmlXPathNewNodeSet(aptr->nodesetptr->nodes[ni]);\r
607                                                         valuePush(xpath_ctxt, n);\r
608                                                         xmlXPathStringFunction(xpath_ctxt, 1);\r
609                                                         a.Add(xmlXPathCastToString(valuePop(xpath_ctxt)));\r
610                                                         xmlXPathFreeObject(n);\r
611                                                 }\r
612                                                 args.Add(a);\r
613                                         } else { // Anything else => string\r
614                                                 valuePush(xpath_ctxt, aptr);\r
615                                                 xmlXPathStringFunction(xpath_ctxt, 1);\r
616                                                 args.Add(xmlXPathCastToString(valuePop(xpath_ctxt)));\r
617                                         }\r
618 \r
619                                         xmlXPathFreeObject(aptr);\r
620                                 }\r
621 \r
622                                 args.Reverse();\r
623 \r
624                                 object ret = func(args.ToArray());\r
625 \r
626                                 // Convert the result back to an XPath object\r
627                                 if (ret == null) // null => ""\r
628                                         valuePush(xpath_ctxt, xmlXPathNewCString(""));\r
629                                 else if (ret is bool) // Booleans\r
630                                         valuePush(xpath_ctxt, xmlXPathNewBoolean((bool)ret ? 1 : 0));\r
631                                 else if (ret is int || ret is long || ret is double || ret is float || ret is decimal)\r
632                                         // Numbers\r
633                                         valuePush(xpath_ctxt, xmlXPathNewFloat((double)ret));\r
634                                 else // Everything else => String\r
635                                         valuePush(xpath_ctxt, xmlXPathNewCString(ret.ToString()));\r
636 \r
637                         }\r
638                 }\r
639 \r
640                 // Provides a delegate for calling a late-bound method of a type with a given name.\r
641                 // Determines method based on types of arguments.\r
642                 private class ReflectedExtensionFunction {\r
643                         System.Type type;\r
644                         object src;\r
645                         string methodname;\r
646                 \r
647                         public ReflectedExtensionFunction(System.Type type, object src, string methodname) { this.type = type; this.src = src; this.methodname = methodname; }\r
648                 \r
649                         public object Function(object[] args) {\r
650                                 // Construct arg type array, and a stringified version in case of problem\r
651                                 System.Type[] argtypes = new System.Type[args.Length];\r
652                                 string argtypelist = null;\r
653                                 for (int i = 0; i < args.Length; i++) {\r
654                                         argtypes[i] = (args[i] == null ? typeof(object) : args[i].GetType() );\r
655 \r
656                                         if (argtypelist != null) argtypelist += ", ";\r
657                                         argtypelist += argtypes[i].FullName;\r
658                                 }\r
659                                 if (argtypelist == null) argtypelist = "";\r
660 \r
661                                 // Find the method\r
662                                 System.Reflection.MethodInfo mi = type.GetMethod(methodname, (src == null ? BF.Static : BF.Instance | BF.Static) | BF.Public, null, argtypes, null);\r
663 \r
664                                 // No method?\r
665                                 if (mi == null) throw new XmlException("No applicable function for " + methodname + " takes (" + argtypelist + ")");\r
666 \r
667                                 if (!mi.IsStatic && src == null) throw new XmlException("Attempt to call static method without instantiated extension object.");\r
668 \r
669                                 // Invoke\r
670                                 return mi.Invoke(src, args);\r
671                         }\r
672                 }\r
673 \r
674                 // Special Mono-specific class that allows static methods of a type to\r
675                 // be bound without needing an instance of that type.  Useful for\r
676                 // registering System.Math functions, for example.\r
677                 // Usage:   args.AddExtensionObject( new XslTransform.UseStaticMethods(typeof(thetype)) );\r
678                 public sealed class UseStaticMethods {\r
679                         public readonly System.Type Type;\r
680                         public UseStaticMethods(System.Type Type) { this.Type = Type; }\r
681                 }\r
682 \r
683                 #endregion\r
684 \r
685                 #region Calls to external libraries\r
686                 // libxslt\r
687                 [DllImport ("xslt")]\r
688                 static extern IntPtr xsltParseStylesheetFile (string filename);\r
689 \r
690                 [DllImport ("xslt")]\r
691                 static extern IntPtr xsltParseStylesheetDoc (IntPtr docPtr);\r
692 \r
693                 [DllImport ("xslt")]\r
694                 static extern IntPtr xsltApplyStylesheet (IntPtr stylePtr, IntPtr DocPtr, string[] argPtr);\r
695 \r
696                 [DllImport ("xslt")]\r
697                 static extern int xsltSaveResultToFilename (string URI, IntPtr doc, IntPtr styleSheet, int compression);\r
698 \r
699                 [DllImport ("xslt")]\r
700                 static extern void xsltCleanupGlobals ();\r
701 \r
702                 [DllImport ("xslt")]\r
703                 static extern void xsltFreeStylesheet (IntPtr cur);\r
704 \r
705                 // libxml2\r
706                 [DllImport ("xml2")]\r
707                 static extern IntPtr xmlNewDoc (string version);\r
708 \r
709                 [DllImport ("xml2")]\r
710                 static extern int xmlSaveFile (string filename, IntPtr cur);\r
711 \r
712                 [DllImport ("xml2")]\r
713                 static extern IntPtr xmlParseFile (string filename);\r
714 \r
715                 [DllImport ("xml2")]\r
716                 static extern IntPtr xmlParseDoc (string document);\r
717 \r
718                 [DllImport ("xml2")]\r
719                 static extern void xmlFreeDoc (IntPtr doc);\r
720 \r
721                 [DllImport ("xml2")]\r
722                 static extern void xmlCleanupParser ();\r
723 \r
724                 [DllImport ("xml2")]\r
725                 static extern void xmlDocDumpMemory (IntPtr doc, ref IntPtr mem, ref int size);\r
726 \r
727                 [DllImport ("xml2")]\r
728                 static extern void xmlFree (IntPtr data);\r
729 \r
730                 // Functions and structures for extension objects\r
731 \r
732                 [DllImport ("xslt")]\r
733                 static extern IntPtr xsltNewTransformContext (IntPtr style, IntPtr doc);\r
734 \r
735                 [DllImport ("xslt")]\r
736                 static extern void xsltFreeTransformContext (IntPtr context);\r
737 \r
738                 [DllImport ("xslt")]\r
739                 static extern IntPtr xsltApplyStylesheetUser (IntPtr stylePtr, IntPtr DocPtr, string[] argPtr, string output, IntPtr profile, IntPtr context);\r
740 \r
741                 [DllImport ("xslt")]\r
742                 static extern int xsltRegisterExtFunction (IntPtr context, string name, string uri, libxsltXPathFunction function);\r
743 \r
744                 [DllImport ("xml2")]\r
745                 unsafe static extern xpathobject* valuePop (IntPtr context);\r
746 \r
747                 [DllImport ("xml2")]\r
748                 unsafe static extern void valuePush (IntPtr context, xpathobject* data);\r
749 \r
750                 [DllImport("xml2")]\r
751                 unsafe static extern void xmlXPathFreeObject(xpathobject* obj);\r
752                 \r
753                 [DllImport("xml2")]\r
754                 unsafe static extern xpathobject* xmlXPathNewCString(string str);\r
755 \r
756                 [DllImport("xml2")]\r
757                 unsafe static extern xpathobject* xmlXPathNewFloat(double val);\r
758 \r
759                 [DllImport("xml2")]\r
760                 unsafe static extern xpathobject* xmlXPathNewBoolean(int val);\r
761 \r
762                 [DllImport("xml2")]\r
763                 unsafe static extern xpathobject* xmlXPathNewNodeSet(IntPtr nodeptr);\r
764 \r
765                 [DllImport("xml2")]\r
766                 unsafe static extern int xmlXPathCastToBoolean(xpathobject* val);\r
767 \r
768                 [DllImport("xml2")]\r
769                 unsafe static extern double xmlXPathCastToNumber(xpathobject* val);\r
770 \r
771                 [DllImport("xml2")]\r
772                 unsafe static extern string xmlXPathCastToString(xpathobject* val);\r
773 \r
774                 [DllImport("xml2")]\r
775                 static extern void xmlXPathStringFunction(IntPtr context, int nargs);\r
776 \r
777                 private delegate void libxsltXPathFunction(IntPtr xpath_ctxt, int nargs);\r
778 \r
779                 private struct xpathobject {\r
780                         public int type;\r
781                         public xmlnodelist* nodesetptr;\r
782                 }\r
783                 private struct xmlnodelist {\r
784                         public int count;\r
785                         public int allocated;\r
786                         public IntPtr* nodes;\r
787                 }\r
788 \r
789                 #endregion\r
790 \r
791                 // This classes just makes the base class use 'encoding="utf-8"'\r
792                 class UTF8StringWriter : StringWriter\r
793                 {\r
794                         static Encoding encoding = new UTF8Encoding (false);\r
795 \r
796                         public override Encoding Encoding {\r
797                                 get {\r
798                                         return encoding;\r
799                                 }\r
800                         }\r
801                 }\r
802         }\r
803 }\r