XslTransform.cs 25 KB

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