| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- // System.Xml.Xsl.XslTransform
- //
- // Authors:
- // Tim Coleman <[email protected]>
- // Gonzalo Paniagua Javier ([email protected])
- //
- // (C) Copyright 2002 Tim Coleman
- // (c) 2003 Ximian Inc. (http://www.ximian.com)
- //
- using System;
- using System.Xml.XPath;
- using System.IO;
- using System.Text;
- using System.Runtime.InteropServices;
- namespace System.Xml.Xsl
- {
- public sealed class XslTransform
- {
- #region Fields
- XmlResolver xmlResolver;
- IntPtr stylesheet;
- #endregion
- #region Constructors
- public XslTransform ()
- {
- stylesheet = IntPtr.Zero;
- }
- #endregion
- #region Properties
- public XmlResolver XmlResolver {
- set { xmlResolver = value; }
- }
- #endregion
- #region Methods
- void FreeStylesheetIfNeeded ()
- {
- if (stylesheet != IntPtr.Zero) {
- xsltFreeStylesheet (stylesheet);
- stylesheet = IntPtr.Zero;
- }
- }
-
- // Loads the XSLT stylesheet contained in the IXPathNavigable.
- public void Load (IXPathNavigable stylesheet)
- {
- Load (stylesheet.CreateNavigator ());
- }
- // Loads the XSLT stylesheet specified by a URL.
- public void Load (string url)
- {
- if (url == null)
- throw new ArgumentNullException ("url");
- FreeStylesheetIfNeeded ();
- stylesheet = xsltParseStylesheetFile (url);
- Cleanup ();
- if (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;
- }
- // Loads the XSLT stylesheet contained in the XmlReader
- public void Load (XmlReader stylesheet)
- {
- 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");
- }
- // Loads the XSLT stylesheet contained in the XPathNavigator
- public void Load (XPathNavigator stylesheet)
- {
- 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");
- }
- [MonoTODO("use the resolver")]
- // Loads the XSLT stylesheet contained in the IXPathNavigable.
- public void Load (IXPathNavigable stylesheet, XmlResolver resolver)
- {
- Load (stylesheet);
- }
- [MonoTODO("use the resolver")]
- // Loads the XSLT stylesheet specified by a URL.
- public void Load (string url, XmlResolver resolver)
- {
- Load (url);
- }
- [MonoTODO("use the resolver")]
- // Loads the XSLT stylesheet contained in the XmlReader
- public void Load (XmlReader stylesheet, XmlResolver resolver)
- {
- Load (stylesheet);
- }
- [MonoTODO("use the resolver")]
- // Loads the XSLT stylesheet contained in the XPathNavigator
- public void Load (XPathNavigator stylesheet, XmlResolver resolver)
- {
- Load (stylesheet);
- }
- // Transforms the XML data in the IXPathNavigable using
- // the specified args and outputs the result to an XmlReader.
- public XmlReader Transform (IXPathNavigable input, XsltArgumentList args)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- return Transform (input.CreateNavigator (), args);
- }
- // Transforms the XML data in the input file and outputs
- // the result to an output file.
- public void Transform (string inputfile, string outputfile)
- {
- 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);
- /*
- * If I do this, the <?xml version=... is always present *
- if (-1 == xsltSaveResultToFilename (outputfile, resultDocument, stylesheet, 0))
- throw new XmlException ("Error xsltSaveResultToFilename");
- */
- StreamWriter writer = new StreamWriter (File.OpenWrite (outputfile));
- writer.Write (GetStringFromDocument (resultDocument));
- writer.Close ();
- } finally {
- if (xmlDocument != IntPtr.Zero)
- xmlFreeDoc (xmlDocument);
- if (resultDocument != IntPtr.Zero)
- xmlFreeDoc (resultDocument);
- Cleanup ();
- }
- }
- IntPtr ApplyStylesheet (IntPtr doc, string[] argArr)
- {
- if (stylesheet == IntPtr.Zero)
- throw new XmlException ("No style sheet!");
- IntPtr result = xsltApplyStylesheet (stylesheet, doc, argArr);
- if (result == IntPtr.Zero)
- throw new XmlException ("Error applying style sheet");
- return result;
- }
- static void Cleanup ()
- {
- xsltCleanupGlobals ();
- xmlCleanupParser ();
- }
- static string GetStringFromDocument (IntPtr doc)
- {
- IntPtr mem = IntPtr.Zero;
- int size = 0;
- xmlDocDumpMemory (doc, ref mem, ref size);
- if (mem == IntPtr.Zero)
- throw new XmlException ("Error dumping document");
- string docStr = Marshal.PtrToStringAnsi (mem, size);
- // FIXME: Using xmlFree segfaults :-???
- //xmlFree (mem);
- Marshal.FreeHGlobal (mem);
- //
- // Get rid of the <?xml...
- // FIXME: any other (faster) way that works?
- StringReader result = new StringReader (docStr);
- result.ReadLine (); // we want the semantics of line ending used here
- //
- return result.ReadToEnd ();
- }
- string ApplyStylesheetAndGetString (IntPtr doc, string[] argArr)
- {
- IntPtr xmlOutput = ApplyStylesheet (doc, argArr);
- string strOutput = GetStringFromDocument (xmlOutput);
- 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;
- }
- [MonoTODO("Node Set and Node Fragment Parameters and Extension Objects")]
- // Transforms the XML data in the XPathNavigator using
- // the specified args and outputs the result to an XmlReader.
- public XmlReader Transform (XPathNavigator input, XsltArgumentList args)
- {
- IntPtr xmlInput = GetDocumentFromNavigator (input);
- string[] argArr = null;
- if (args != null) {
- 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 xslOutputString = ApplyStylesheetAndGetString (xmlInput, argArr);
- xmlFreeDoc (xmlInput);
- Cleanup ();
- return new XmlTextReader (new StringReader (xslOutputString));
- }
- // Transforms the XML data in the IXPathNavigable using
- // the specified args and outputs the result to a Stream.
- public void Transform (IXPathNavigable input, XsltArgumentList args, Stream output)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- Transform (input.CreateNavigator (), args, new StreamWriter (output));
- }
- // Transforms the XML data in the IXPathNavigable using
- // the specified args and outputs the result to a TextWriter.
- public void Transform (IXPathNavigable input, XsltArgumentList args, TextWriter output)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- Transform (input.CreateNavigator (), args, output);
- }
- // Transforms the XML data in the IXPathNavigable using
- // the specified args and outputs the result to an XmlWriter.
- public void Transform (IXPathNavigable input, XsltArgumentList args, XmlWriter output)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- Transform (input.CreateNavigator (), args, output);
- }
- // Transforms the XML data in the XPathNavigator using
- // the specified args and outputs the result to a Stream.
- public void Transform (XPathNavigator input, XsltArgumentList args, Stream output)
- {
- Transform (input, args, new StreamWriter (output));
- }
- // Transforms the XML data in the XPathNavigator using
- // the specified args and outputs the result to a TextWriter.
- [MonoTODO("Node Set and Node Fragment Parameters and Extension Objects")]
- public void Transform (XPathNavigator input, XsltArgumentList args, TextWriter output)
- {
- if (input == null)
- throw new ArgumentNullException ("input");
- if (output == null)
- throw new ArgumentNullException ("output");
- IntPtr inputDoc = GetDocumentFromNavigator (input);
- string[] argArr = null;
- if (args != null) {
- 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);
- xmlFreeDoc (inputDoc);
- Cleanup ();
- output.Write (transform);
- }
- // Transforms the XML data in the XPathNavigator using
- // the specified args and outputs the result to an XmlWriter.
- public void Transform (XPathNavigator input, XsltArgumentList args, XmlWriter output)
- {
- StringWriter writer = new UTF8StringWriter ();
- Transform (input, args, writer);
- output.WriteRaw (writer.GetStringBuilder ().ToString ());
- }
- 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.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.Attribute:
- writer.WriteAttributeString (navigator.LocalName, 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;
- }
- }
- #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 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);
- #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;
- }
- }
- }
- }
- }
|