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