| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- // UnmanagedXslTransform
- //
- // Authors:
- // Tim Coleman <[email protected]>
- // Gonzalo Paniagua Javier ([email protected])
- // Ben Maurer ([email protected])
- //
- // (C) Copyright 2002 Tim Coleman
- // (c) 2003 Ximian Inc. (http://www.ximian.com)
- // (C) Ben Maurer 2003
- //
- // DO NOT MOVE THIS FILE. WE WANT HISTORY
- using System;
- using System.Collections;
- using System.IO;
- using System.Text;
- using System.Runtime.InteropServices;
- using System.Xml.XPath;
- using BF = System.Reflection.BindingFlags;
- namespace System.Xml.Xsl
- {
- internal unsafe sealed class UnmanagedXslTransform : XslTransformImpl
- {
- #region Fields
-
- IntPtr stylesheet;
- Hashtable extensionObjectCache = new Hashtable();
- #endregion
- #region Constructors
- public UnmanagedXslTransform ()
- {
- stylesheet = IntPtr.Zero;
- }
- #endregion
- #region Methods
- ~UnmanagedXslTransform ()
- {
- FreeStylesheetIfNeeded ();
- }
- void FreeStylesheetIfNeeded ()
- {
- if (stylesheet != IntPtr.Zero) {
- xsltFreeStylesheet (stylesheet);
- stylesheet = IntPtr.Zero;
- }
- }
- public override void Load (string url, XmlResolver resolver)
- {
- FreeStylesheetIfNeeded ();
- stylesheet = xsltParseStylesheetFile (url);
- Cleanup ();
- if (stylesheet == IntPtr.Zero)
- throw new XmlException ("Error creating stylesheet");
- }
-
- public override void Load (XmlReader stylesheet, XmlResolver resolver)
- {
- FreeStylesheetIfNeeded ();
- // Create a document for the stylesheet
- XmlDocument doc = new XmlDocument ();
- doc.Load (stylesheet);
-
- // Store the XML in a StringBuilder
- StringWriter sr = new UTF8StringWriter ();
- XmlTextWriter writer = new XmlTextWriter (sr);
- doc.Save (writer);
- this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());
- Cleanup ();
- if (this.stylesheet == IntPtr.Zero)
- throw new XmlException ("Error creating stylesheet");
- }
- public override void Load (XPathNavigator stylesheet, XmlResolver resolver)
- {
- FreeStylesheetIfNeeded ();
- StringWriter sr = new UTF8StringWriter ();
- Save (stylesheet, sr);
- this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());
- Cleanup ();
- if (this.stylesheet == IntPtr.Zero)
- throw new XmlException ("Error creating stylesheet");
- }
-
- static IntPtr GetStylesheetFromString (string xml)
- {
- IntPtr result = IntPtr.Zero;
- IntPtr xmlDoc = xmlParseDoc (xml);
- if (xmlDoc == IntPtr.Zero) {
- Cleanup ();
- throw new XmlException ("Error parsing stylesheet");
- }
-
- result = xsltParseStylesheetDoc (xmlDoc);
- Cleanup ();
- if (result == IntPtr.Zero)
- throw new XmlException ("Error creating stylesheet");
- return result;
- }
- IntPtr ApplyStylesheet (IntPtr doc, string[] argArr, Hashtable extobjects)
- {
- if (stylesheet == IntPtr.Zero)
- throw new XmlException ("No style sheet!");
- IntPtr result;
- if (extobjects == null || extobjects.Count == 0) {
- // If there are no extension objects, use the simple (old) method.
- result = xsltApplyStylesheet (stylesheet, doc, argArr);
- } else {
- // If there are extension objects, create a context and register the functions.
- IntPtr context = xsltNewTransformContext(stylesheet, doc);
- if (context == IntPtr.Zero) throw new XmlException("Error creating transformation context.");
- try {
- foreach (string ns in extobjects.Keys) {
- object ext = extobjects[ns];
- if (extensionObjectCache.ContainsKey(ext)) {
- foreach (ExtensionFunctionHolder ef in (ArrayList)extensionObjectCache[ext]) {
- int ret = xsltRegisterExtFunction(context, ef.name, ef.ns, ef.func);
- if (ret != 0) throw new XmlException("Could not reregister extension function " + ef.name + " in " + ef.ns);
- }
- } else {
- object extsrc;
-
- System.Type type;
- System.Collections.IEnumerable methods;
-
- // As an added bonus, if the extension object is a UseStaticMethods object
- // (defined below), then add the static methods of the specified type.
- if (ext is UseStaticMethods) {
- type = ((UseStaticMethods)ext).Type;
- methods = type.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
- extsrc = null;
- } else {
- extsrc = ext;
- type = ext.GetType();
- methods = type.GetMethods();
- }
- ArrayList functionstocache = new ArrayList();
-
- Hashtable alreadyadded = new Hashtable ();
- foreach (System.Reflection.MethodInfo mi in methods) {
- if (alreadyadded.ContainsKey(mi.Name)) continue; // don't add twice
- alreadyadded[mi.Name] = 1;
- // Simple extension function delegate
- ExtensionFunction func = new ExtensionFunction(new ReflectedExtensionFunction(type, extsrc, mi.Name).Function);
-
- // Delegate for libxslt library call
- libxsltXPathFunction libfunc = new libxsltXPathFunction(new ExtensionFunctionWrapper(func).Function);
-
- int ret = xsltRegisterExtFunction(context, mi.Name, ns, libfunc);
- if (ret != 0) throw new XmlException("Could not register extension function " + mi.DeclaringType.FullName + "." + mi.Name + " in " + ns);
- ExtensionFunctionHolder efh;
- efh.name = mi.Name;
- efh.ns = ns;
- efh.func = libfunc;
- functionstocache.Add(efh);
- }
- extensionObjectCache[ext] = functionstocache;
- }
-
- }
-
- result = xsltApplyStylesheetUser(stylesheet, doc, argArr, null, IntPtr.Zero, context);
- } finally {
- xsltFreeTransformContext(context);
- }
- }
- if (result == IntPtr.Zero)
- throw new XmlException ("Error applying style sheet");
- return result;
- }
- static void Cleanup ()
- {
- xsltCleanupGlobals ();
- xmlCleanupParser ();
- }
- static string GetStringFromDocument (IntPtr doc, IntPtr stylesheet)
- {
- IntPtr mem = IntPtr.Zero;
- int size = 0;
- int res = xsltSaveResultToString (ref mem, ref size, doc,
- stylesheet);
- if (res == -1)
- throw new XmlException ("xsltSaveResultToString () failed.");
- string docStr = Marshal.PtrToStringAnsi (mem, size);
- Marshal.FreeHGlobal (mem);
- return docStr;
- }
- string ApplyStylesheetAndGetString (IntPtr doc, string[] argArr, Hashtable extobjects)
- {
- IntPtr xmlOutput = ApplyStylesheet (doc, argArr, extobjects);
- string strOutput = GetStringFromDocument (xmlOutput, stylesheet);
- xmlFreeDoc (xmlOutput);
- return strOutput;
- }
- IntPtr GetDocumentFromNavigator (XPathNavigator nav)
- {
- StringWriter sr = new UTF8StringWriter ();
- Save (nav, sr);
- IntPtr xmlInput = xmlParseDoc (sr.GetStringBuilder ().ToString ());
- if (xmlInput == IntPtr.Zero)
- throw new XmlException ("Error getting XML from input");
- return xmlInput;
- }
- public override void Transform (XPathNavigator input, XsltArgumentList args, XmlWriter output, XmlResolver resolver)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- if (output == null)
- throw new ArgumentNullException ("output");
- StringWriter writer = new UTF8StringWriter ();
-
- IntPtr inputDoc = GetDocumentFromNavigator (input);
- string[] argArr = null;
- Hashtable extensionObjects = null;
- if (args != null) {
- extensionObjects = args.extensionObjects;
- argArr = new string[args.parameters.Count * 2 + 1];
- int index = 0;
- foreach (object key in args.parameters.Keys) {
- argArr [index++] = key.ToString();
- object value = args.parameters [key];
- if (value is Boolean)
- argArr [index++] = XmlConvert.ToString((bool) value); // FIXME: How to encode it for libxslt?
- else if (value is Double)
- argArr [index++] = XmlConvert.ToString((double) value); // FIXME: How to encode infinity's and Nan?
- else
- argArr [index++] = "'" + value.ToString() + "'"; // FIXME: How to encode "'"?
- }
- argArr[index] = null;
- }
- string transform = ApplyStylesheetAndGetString (inputDoc, argArr, extensionObjects);
- xmlFreeDoc (inputDoc);
- Cleanup ();
- writer.Write (transform);
- writer.Flush ();
-
- output.WriteRaw (writer.GetStringBuilder ().ToString ());
- output.Flush ();
- }
- public override void Transform(string inputfile, string outputfile, XmlResolver resolver)
- {
- IntPtr xmlDocument = IntPtr.Zero;
- IntPtr resultDocument = IntPtr.Zero;
- try {
- xmlDocument = xmlParseFile (inputfile);
- if (xmlDocument == IntPtr.Zero)
- throw new XmlException ("Error parsing input file");
- resultDocument = ApplyStylesheet (xmlDocument, null, null);
- if (-1 == xsltSaveResultToFilename (outputfile, resultDocument, stylesheet, 0))
- throw new XmlException ("Error in xsltSaveResultToFilename");
- } finally {
- if (xmlDocument != IntPtr.Zero)
- xmlFreeDoc (xmlDocument);
- if (resultDocument != IntPtr.Zero)
- xmlFreeDoc (resultDocument);
- Cleanup ();
- }
- }
- static void Save (XmlReader rdr, TextWriter baseWriter)
- {
- XmlTextWriter writer = new XmlTextWriter (baseWriter);
-
- while (rdr.Read ()) {
- switch (rdr.NodeType) {
-
- case XmlNodeType.CDATA:
- writer.WriteCData (rdr.Value);
- break;
-
- case XmlNodeType.Comment:
- writer.WriteComment (rdr.Value);
- break;
- case XmlNodeType.DocumentType:
- writer.WriteDocType (rdr.Value, null, null, null);
- break;
- case XmlNodeType.Element:
- writer.WriteStartElement (rdr.Name, rdr.Value);
-
- while (rdr.MoveToNextAttribute ())
- writer.WriteAttributes (rdr, true);
- break;
-
- case XmlNodeType.EndElement:
- writer.WriteEndElement ();
- break;
- case XmlNodeType.ProcessingInstruction:
- writer.WriteProcessingInstruction (rdr.Name, rdr.Value);
- break;
- case XmlNodeType.Text:
- writer.WriteString (rdr.Value);
- break;
- case XmlNodeType.Whitespace:
- writer.WriteWhitespace (rdr.Value);
- break;
- case XmlNodeType.XmlDeclaration:
- writer.WriteStartDocument ();
- break;
- }
- }
- writer.Close ();
- }
- static void Save (XPathNavigator navigator, TextWriter writer)
- {
- XmlTextWriter xmlWriter = new XmlTextWriter (writer);
- WriteTree (navigator, xmlWriter);
- xmlWriter.WriteEndDocument ();
- xmlWriter.Flush ();
- }
- // Walks the XPathNavigator tree recursively
- static void WriteTree (XPathNavigator navigator, XmlTextWriter writer)
- {
- WriteCurrentNode (navigator, writer);
- if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
- do {
- WriteCurrentNode (navigator, writer);
- } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
- navigator.MoveToParent ();
- }
- if (navigator.MoveToFirstAttribute ()) {
- do {
- WriteCurrentNode (navigator, writer);
- } while (navigator.MoveToNextAttribute ());
- navigator.MoveToParent ();
- }
- if (navigator.MoveToFirstChild ()) {
- do {
- WriteTree (navigator, writer);
- } while (navigator.MoveToNext ());
- navigator.MoveToParent ();
- if (navigator.NodeType != XPathNodeType.Root)
- writer.WriteEndElement ();
- } else if (navigator.NodeType == XPathNodeType.Element) {
- writer.WriteEndElement ();
- }
- }
- // Format the output
- static void WriteCurrentNode (XPathNavigator navigator, XmlTextWriter writer)
- {
- switch (navigator.NodeType) {
- case XPathNodeType.Root:
- writer.WriteStartDocument ();
- break;
- case XPathNodeType.Namespace:
- if (navigator.Name == String.Empty)
- writer.WriteAttributeString ("xmlns", navigator.Value);
- else
- writer.WriteAttributeString ("xmlns",
- navigator.Name,
- "http://www.w3.org/2000/xmlns/",
- navigator.Value);
- break;
- case XPathNodeType.Attribute:
- writer.WriteAttributeString (navigator.Name, navigator.Value);
- break;
- case XPathNodeType.Comment:
- writer.WriteComment (navigator.Value);
- break;
- case XPathNodeType.Element:
- writer.WriteStartElement (navigator.Name);
- break;
-
- case XPathNodeType.ProcessingInstruction:
- writer.WriteProcessingInstruction (navigator.Name, navigator.Value);
- break;
- case XPathNodeType.Text:
- writer.WriteString (navigator.Value);
- break;
- case XPathNodeType.SignificantWhitespace:
- case XPathNodeType.Whitespace:
- writer.WriteWhitespace (navigator.Value);
- break;
- }
- }
- // Extension Objects
- internal delegate object ExtensionFunction(object[] args);
- private struct ExtensionFunctionHolder {
- public libxsltXPathFunction func;
- public string ns, name;
- }
- // Wraps an ExtensionFunction into a function that is callable from the libxslt library.
- private unsafe class ExtensionFunctionWrapper {
- private readonly ExtensionFunction func;
- public ExtensionFunctionWrapper(ExtensionFunction func) {
- if ((object)func == null) throw new ArgumentNullException("func");
- this.func = func;
- }
- public unsafe void Function(IntPtr xpath_ctxt, int nargs) {
- // Convert XPath arguments into "managed" arguments
- System.Collections.ArrayList args = new System.Collections.ArrayList();
- for (int i = 0; i < nargs; i++) {
- xpathobject* aptr = valuePop(xpath_ctxt);
- if (aptr->type == 2) // Booleans
- args.Add( xmlXPathCastToBoolean(aptr) == 0 ? false : true );
- else if (aptr->type == 3) // Doubles
- args.Add( xmlXPathCastToNumber(aptr));
- else if (aptr->type == 4) // Strings
- args.Add( xmlXPathCastToString(aptr));
- else if (aptr->type == 1 && aptr->nodesetptr != null) { // Node Sets ==> ArrayList of strings
- System.Collections.ArrayList a = new System.Collections.ArrayList();
- for (int ni = 0; ni < aptr->nodesetptr->count; ni++) {
- xpathobject *n = xmlXPathNewNodeSet(aptr->nodesetptr->nodes[ni]);
- valuePush(xpath_ctxt, n);
- xmlXPathStringFunction(xpath_ctxt, 1);
- a.Add(xmlXPathCastToString(valuePop(xpath_ctxt)));
- xmlXPathFreeObject(n);
- }
- args.Add(a);
- } else { // Anything else => string
- valuePush(xpath_ctxt, aptr);
- xmlXPathStringFunction(xpath_ctxt, 1);
- args.Add(xmlXPathCastToString(valuePop(xpath_ctxt)));
- }
- xmlXPathFreeObject(aptr);
- }
- args.Reverse();
- object ret = func(args.ToArray());
- // Convert the result back to an XPath object
- if (ret == null) // null => ""
- valuePush(xpath_ctxt, xmlXPathNewCString(""));
- else if (ret is bool) // Booleans
- valuePush(xpath_ctxt, xmlXPathNewBoolean((bool)ret ? 1 : 0));
- else if (ret is int || ret is long || ret is double || ret is float || ret is decimal)
- // Numbers
- valuePush(xpath_ctxt, xmlXPathNewFloat((double)ret));
- else // Everything else => String
- valuePush(xpath_ctxt, xmlXPathNewCString(ret.ToString()));
- }
- }
- // Provides a delegate for calling a late-bound method of a type with a given name.
- // Determines method based on types of arguments.
- private class ReflectedExtensionFunction {
- System.Type type;
- object src;
- string methodname;
-
- public ReflectedExtensionFunction(System.Type type, object src, string methodname) { this.type = type; this.src = src; this.methodname = methodname; }
-
- public object Function(object[] args) {
- // Construct arg type array, and a stringified version in case of problem
- System.Type[] argtypes = new System.Type[args.Length];
- string argtypelist = null;
- for (int i = 0; i < args.Length; i++) {
- argtypes[i] = (args[i] == null ? typeof(object) : args[i].GetType() );
- if (argtypelist != null) argtypelist += ", ";
- argtypelist += argtypes[i].FullName;
- }
- if (argtypelist == null) argtypelist = "";
- // Find the method
- System.Reflection.MethodInfo mi = type.GetMethod(methodname, (src == null ? BF.Static : BF.Instance | BF.Static) | BF.Public, null, argtypes, null);
- // No method?
- if (mi == null) throw new XmlException("No applicable function for " + methodname + " takes (" + argtypelist + ")");
- if (!mi.IsStatic && src == null) throw new XmlException("Attempt to call static method without instantiated extension object.");
- // Invoke
- return mi.Invoke(src, args);
- }
- }
- // Special Mono-specific class that allows static methods of a type to
- // be bound without needing an instance of that type. Useful for
- // registering System.Math functions, for example.
- // Usage: args.AddExtensionObject( new XslTransform.UseStaticMethods(typeof(thetype)) );
- public sealed class UseStaticMethods {
- public readonly System.Type Type;
- public UseStaticMethods(System.Type Type) { this.Type = Type; }
- }
- #endregion
- #region Calls to external libraries
- // libxslt
- [DllImport ("xslt")]
- static extern IntPtr xsltParseStylesheetFile (string filename);
- [DllImport ("xslt")]
- static extern IntPtr xsltParseStylesheetDoc (IntPtr docPtr);
- [DllImport ("xslt")]
- static extern IntPtr xsltApplyStylesheet (IntPtr stylePtr, IntPtr DocPtr, string[] argPtr);
- [DllImport ("xslt")]
- static extern int xsltSaveResultToString (ref IntPtr stringPtr, ref int stringLen,
- IntPtr docPtr, IntPtr stylePtr);
- [DllImport ("xslt")]
- static extern int xsltSaveResultToFilename (string URI, IntPtr doc, IntPtr styleSheet, int compression);
- [DllImport ("xslt")]
- static extern void xsltCleanupGlobals ();
- [DllImport ("xslt")]
- static extern void xsltFreeStylesheet (IntPtr cur);
- // libxml2
- [DllImport ("xml2")]
- static extern IntPtr xmlNewDoc (string version);
- [DllImport ("xml2")]
- static extern int xmlSaveFile (string filename, IntPtr cur);
- [DllImport ("xml2")]
- static extern IntPtr xmlParseFile (string filename);
- [DllImport ("xml2")]
- static extern IntPtr xmlParseDoc (string document);
- [DllImport ("xml2")]
- static extern void xmlFreeDoc (IntPtr doc);
- [DllImport ("xml2")]
- static extern void xmlCleanupParser ();
- [DllImport ("xml2")]
- static extern void xmlDocDumpMemory (IntPtr doc, ref IntPtr mem, ref int size);
- [DllImport ("xml2")]
- static extern void xmlFree (IntPtr data);
- // Functions and structures for extension objects
- [DllImport ("xslt")]
- static extern IntPtr xsltNewTransformContext (IntPtr style, IntPtr doc);
- [DllImport ("xslt")]
- static extern void xsltFreeTransformContext (IntPtr context);
- [DllImport ("xslt")]
- static extern IntPtr xsltApplyStylesheetUser (IntPtr stylePtr, IntPtr DocPtr, string[] argPtr, string output, IntPtr profile, IntPtr context);
- [DllImport ("xslt")]
- static extern int xsltRegisterExtFunction (IntPtr context, string name, string uri, libxsltXPathFunction function);
- [DllImport ("xml2")]
- unsafe static extern xpathobject* valuePop (IntPtr context);
- [DllImport ("xml2")]
- unsafe static extern void valuePush (IntPtr context, xpathobject* data);
- [DllImport("xml2")]
- unsafe static extern void xmlXPathFreeObject(xpathobject* obj);
-
- [DllImport("xml2")]
- unsafe static extern xpathobject* xmlXPathNewCString(string str);
- [DllImport("xml2")]
- unsafe static extern xpathobject* xmlXPathNewFloat(double val);
- [DllImport("xml2")]
- unsafe static extern xpathobject* xmlXPathNewBoolean(int val);
- [DllImport("xml2")]
- unsafe static extern xpathobject* xmlXPathNewNodeSet(IntPtr nodeptr);
- [DllImport("xml2")]
- unsafe static extern int xmlXPathCastToBoolean(xpathobject* val);
- [DllImport("xml2")]
- unsafe static extern double xmlXPathCastToNumber(xpathobject* val);
- [DllImport("xml2")]
- unsafe static extern string xmlXPathCastToString(xpathobject* val);
- [DllImport("xml2")]
- static extern void xmlXPathStringFunction(IntPtr context, int nargs);
- private delegate void libxsltXPathFunction(IntPtr xpath_ctxt, int nargs);
- private struct xpathobject {
- public int type;
- public xmlnodelist* nodesetptr;
- }
- private struct xmlnodelist {
- public int count;
- public int allocated;
- public IntPtr* nodes;
- }
- #endregion
- // This classes just makes the base class use 'encoding="utf-8"'
- class UTF8StringWriter : StringWriter
- {
- static Encoding encoding = new UTF8Encoding (false);
- public override Encoding Encoding {
- get {
- return encoding;
- }
- }
- }
- }
- }
|