1 // System.Xml.Xsl.XslTransform
\r
4 // Tim Coleman <tim@timcoleman.com>
\r
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
\r
7 // (C) Copyright 2002 Tim Coleman
\r
8 // (c) 2003 Ximian Inc. (http://www.ximian.com)
\r
12 using System.Xml.XPath;
\r
15 using System.Runtime.InteropServices;
\r
17 namespace System.Xml.Xsl
\r
19 public sealed class XslTransform
\r
24 XmlResolver xmlResolver;
\r
29 #region Constructors
\r
30 public XslTransform ()
\r
32 stylesheet = IntPtr.Zero;
\r
39 public XmlResolver XmlResolver {
\r
40 set { xmlResolver = value; }
\r
47 void FreeStylesheetIfNeeded ()
\r
49 if (stylesheet != IntPtr.Zero) {
\r
50 xsltFreeStylesheet (stylesheet);
\r
51 stylesheet = IntPtr.Zero;
\r
55 // Loads the XSLT stylesheet contained in the IXPathNavigable.
\r
56 public void Load (IXPathNavigable stylesheet)
\r
58 Load (stylesheet.CreateNavigator ());
\r
61 // Loads the XSLT stylesheet specified by a URL.
\r
62 public void Load (string url)
\r
65 throw new ArgumentNullException ("url");
\r
67 FreeStylesheetIfNeeded ();
\r
68 stylesheet = xsltParseStylesheetFile (url);
\r
70 if (stylesheet == IntPtr.Zero)
\r
71 throw new XmlException ("Error creating stylesheet");
\r
74 static IntPtr GetStylesheetFromString (string xml)
\r
76 IntPtr result = IntPtr.Zero;
\r
78 IntPtr xmlDoc = xmlParseDoc (xml);
\r
80 if (xmlDoc == IntPtr.Zero) {
\r
82 throw new XmlException ("Error parsing stylesheet");
\r
85 result = xsltParseStylesheetDoc (xmlDoc);
\r
86 xmlFreeDoc (xmlDoc);
\r
88 if (result == IntPtr.Zero)
\r
89 throw new XmlException ("Error creating stylesheet");
\r
94 // Loads the XSLT stylesheet contained in the XmlReader
\r
95 public void Load (XmlReader stylesheet)
\r
97 FreeStylesheetIfNeeded ();
\r
98 // Create a document for the stylesheet
\r
99 XmlDocument doc = new XmlDocument ();
\r
100 doc.Load (stylesheet);
\r
102 // Store the XML in a StringBuilder
\r
103 StringWriter sr = new UTF8StringWriter ();
\r
104 XmlTextWriter writer = new XmlTextWriter (sr);
\r
107 this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());
\r
110 // Loads the XSLT stylesheet contained in the XPathNavigator
\r
111 public void Load (XPathNavigator stylesheet)
\r
113 FreeStylesheetIfNeeded ();
\r
114 StringWriter sr = new UTF8StringWriter ();
\r
115 Save (stylesheet, sr);
\r
116 this.stylesheet = GetStylesheetFromString (sr.GetStringBuilder ().ToString ());
\r
119 [MonoTODO("use the resolver")]
\r
120 // Loads the XSLT stylesheet contained in the IXPathNavigable.
\r
121 public void Load (IXPathNavigable stylesheet, XmlResolver resolver)
\r
126 [MonoTODO("use the resolver")]
\r
127 // Loads the XSLT stylesheet specified by a URL.
\r
128 public void Load (string url, XmlResolver resolver)
\r
133 [MonoTODO("use the resolver")]
\r
134 // Loads the XSLT stylesheet contained in the XmlReader
\r
135 public void Load (XmlReader stylesheet, XmlResolver resolver)
\r
140 [MonoTODO("use the resolver")]
\r
141 // Loads the XSLT stylesheet contained in the XPathNavigator
\r
142 public void Load (XPathNavigator stylesheet, XmlResolver resolver)
\r
147 // Transforms the XML data in the IXPathNavigable using
\r
148 // the specified args and outputs the result to an XmlReader.
\r
149 public XmlReader Transform (IXPathNavigable input, XsltArgumentList args)
\r
152 throw new ArgumentNullException ("input");
\r
154 return Transform (input.CreateNavigator (), args);
\r
157 // Transforms the XML data in the input file and outputs
\r
158 // the result to an output file.
\r
159 public void Transform (string inputfile, string outputfile)
\r
161 IntPtr xmlDocument = IntPtr.Zero;
\r
162 IntPtr resultDocument = IntPtr.Zero;
\r
165 xmlDocument = xmlParseFile (inputfile);
\r
166 if (xmlDocument == IntPtr.Zero)
\r
167 throw new XmlException ("Error parsing input file");
\r
169 resultDocument = ApplyStylesheet (xmlDocument);
\r
171 * If I do this, the <?xml version=... is always present *
\r
172 if (-1 == xsltSaveResultToFilename (outputfile, resultDocument, stylesheet, 0))
\r
173 throw new XmlException ("Error xsltSaveResultToFilename");
\r
175 StreamWriter writer = new StreamWriter (File.OpenWrite (outputfile));
\r
176 writer.Write (GetStringFromDocument (resultDocument));
\r
179 if (xmlDocument != IntPtr.Zero);
\r
180 xmlFreeDoc (xmlDocument);
\r
182 if (resultDocument != IntPtr.Zero);
\r
183 xmlFreeDoc (resultDocument);
\r
189 IntPtr ApplyStylesheet (IntPtr doc)
\r
191 if (stylesheet == IntPtr.Zero)
\r
192 throw new XmlException ("No style sheet!");
\r
194 IntPtr result = xsltApplyStylesheet (stylesheet, doc, IntPtr.Zero);
\r
195 if (result == IntPtr.Zero)
\r
196 throw new XmlException ("Error applying style sheet");
\r
201 static void Cleanup ()
\r
203 xsltCleanupGlobals ();
\r
204 xmlCleanupParser ();
\r
207 static string GetStringFromDocument (IntPtr doc)
\r
209 IntPtr mem = IntPtr.Zero;
\r
211 xmlDocDumpMemory (doc, ref mem, ref size);
\r
212 if (mem == IntPtr.Zero)
\r
213 throw new XmlException ("Error dumping document");
\r
215 string docStr = Marshal.PtrToStringAnsi (mem, size);
\r
216 // FIXME: Using xmlFree segfaults :-???
\r
218 Marshal.FreeHGlobal (mem);
\r
221 // Get rid of the <?xml...
\r
222 // FIXME: any other (faster) way that works?
\r
223 StringReader result = new StringReader (docStr);
\r
224 result.ReadLine (); // we want the semantics of line ending used here
\r
226 return result.ReadToEnd ();
\r
229 string ApplyStylesheetAndGetString (IntPtr doc)
\r
231 IntPtr xmlOutput = ApplyStylesheet (doc);
\r
232 string strOutput = GetStringFromDocument (xmlOutput);
\r
233 xmlFreeDoc (xmlOutput);
\r
238 IntPtr GetDocumentFromNavigator (XPathNavigator nav)
\r
240 StringWriter sr = new UTF8StringWriter ();
\r
242 IntPtr xmlInput = xmlParseDoc (sr.GetStringBuilder ().ToString ());
\r
243 if (xmlInput == IntPtr.Zero)
\r
244 throw new XmlException ("Error getting XML from input");
\r
250 // Transforms the XML data in the XPathNavigator using
\r
251 // the specified args and outputs the result to an XmlReader.
\r
252 public XmlReader Transform (XPathNavigator input, XsltArgumentList args)
\r
254 IntPtr xmlInput = GetDocumentFromNavigator (input);
\r
255 string xslOutputString = ApplyStylesheetAndGetString (xmlInput);
\r
256 xmlFreeDoc (xmlInput);
\r
259 return new XmlTextReader (xslOutputString);
\r
262 // Transforms the XML data in the IXPathNavigable using
\r
263 // the specified args and outputs the result to a Stream.
\r
264 public void Transform (IXPathNavigable input, XsltArgumentList args, Stream output)
\r
267 throw new ArgumentNullException ("input");
\r
269 Transform (input.CreateNavigator (), args, new StreamWriter (output));
\r
272 // Transforms the XML data in the IXPathNavigable using
\r
273 // the specified args and outputs the result to a TextWriter.
\r
274 public void Transform (IXPathNavigable input, XsltArgumentList args, TextWriter output)
\r
277 throw new ArgumentNullException ("input");
\r
279 Transform (input.CreateNavigator (), args, output);
\r
282 // Transforms the XML data in the IXPathNavigable using
\r
283 // the specified args and outputs the result to an XmlWriter.
\r
284 public void Transform (IXPathNavigable input, XsltArgumentList args, XmlWriter output)
\r
286 StringWriter writer = new UTF8StringWriter ();
\r
287 Transform (input, args, writer);
\r
288 output.WriteRaw (writer.GetStringBuilder ().ToString ());
\r
291 // Transforms the XML data in the XPathNavigator using
\r
292 // the specified args and outputs the result to a Stream.
\r
293 public void Transform (XPathNavigator input, XsltArgumentList args, Stream output)
\r
295 Transform (input, args, new StreamWriter (output));
\r
298 // Transforms the XML data in the XPathNavigator using
\r
299 // the specified args and outputs the result to a TextWriter.
\r
300 public void Transform (XPathNavigator input, XsltArgumentList args, TextWriter output)
\r
303 throw new ArgumentNullException ("input");
\r
305 if (output == null)
\r
306 throw new ArgumentNullException ("output");
\r
308 IntPtr inputDoc = GetDocumentFromNavigator (input);
\r
309 string transform = ApplyStylesheetAndGetString (inputDoc);
\r
310 xmlFreeDoc (inputDoc);
\r
312 output.Write (transform);
\r
315 // Transforms the XML data in the XPathNavigator using
\r
316 // the specified args and outputs the result to an XmlWriter.
\r
317 public void Transform (XPathNavigator input, XsltArgumentList args, XmlWriter output)
\r
319 StringWriter writer = new UTF8StringWriter ();
\r
320 Transform (input, args, writer);
\r
321 output.WriteRaw (writer.GetStringBuilder ().ToString ());
\r
324 static void Save (XmlReader rdr, TextWriter baseWriter)
\r
326 XmlTextWriter writer = new XmlTextWriter (baseWriter);
\r
328 while (rdr.Read ()) {
\r
329 switch (rdr.NodeType) {
\r
331 case XmlNodeType.CDATA:
\r
332 writer.WriteCData (rdr.Value);
\r
335 case XmlNodeType.Comment:
\r
336 writer.WriteComment (rdr.Value);
\r
339 case XmlNodeType.DocumentType:
\r
340 writer.WriteDocType (rdr.Value, null, null, null);
\r
343 case XmlNodeType.Element:
\r
344 writer.WriteStartElement (rdr.Name, rdr.Value);
\r
346 while (rdr.MoveToNextAttribute ())
\r
347 writer.WriteAttributes (rdr, true);
\r
350 case XmlNodeType.EndElement:
\r
351 writer.WriteEndElement ();
\r
354 case XmlNodeType.ProcessingInstruction:
\r
355 writer.WriteProcessingInstruction (rdr.Name, rdr.Value);
\r
358 case XmlNodeType.Text:
\r
359 writer.WriteString (rdr.Value);
\r
362 case XmlNodeType.Whitespace:
\r
363 writer.WriteWhitespace (rdr.Value);
\r
366 case XmlNodeType.XmlDeclaration:
\r
367 writer.WriteStartDocument ();
\r
375 static void Save (XPathNavigator navigator, TextWriter writer)
\r
377 XmlTextWriter xmlWriter = new XmlTextWriter (writer);
\r
378 XPathNodeType type = XPathNodeType.All;
\r
380 WriteTree (navigator, xmlWriter, type);
\r
381 xmlWriter.WriteEndDocument ();
\r
382 xmlWriter.Flush ();
\r
385 // Walks the XPathNavigator tree recursively
\r
386 static void WriteTree (XPathNavigator navigator, XmlTextWriter writer, XPathNodeType type)
\r
388 WriteCurrentNode (navigator, writer, ref type);
\r
390 if (navigator.MoveToFirstAttribute ()) {
\r
392 WriteCurrentNode (navigator, writer, ref type);
\r
393 } while (navigator.MoveToNextAttribute ());
\r
395 navigator.MoveToParent ();
\r
398 if (navigator.MoveToFirstChild ()) {
\r
400 WriteTree (navigator, writer, type);
\r
401 } while (navigator.MoveToNext ());
\r
403 navigator.MoveToParent ();
\r
404 if (navigator.NodeType != XPathNodeType.Root)
\r
405 writer.WriteEndElement ();
\r
409 // Format the output
\r
410 static void WriteCurrentNode (XPathNavigator navigator, XmlTextWriter writer, ref XPathNodeType current_type)
\r
412 switch (navigator.NodeType) {
\r
413 case XPathNodeType.Root:
\r
414 current_type = XPathNodeType.Root;
\r
415 writer.WriteStartDocument ();
\r
417 case XPathNodeType.Attribute:
\r
418 current_type = XPathNodeType.Attribute;
\r
419 writer.WriteAttributeString (navigator.LocalName, navigator.Value);
\r
422 case XPathNodeType.Comment:
\r
423 writer.WriteComment (navigator.Value);
\r
426 case XPathNodeType.Element:
\r
427 current_type = XPathNodeType.Element;
\r
428 writer.WriteStartElement (navigator.Name);
\r
431 case XPathNodeType.ProcessingInstruction:
\r
432 writer.WriteProcessingInstruction (navigator.Name, navigator.Value);
\r
435 case XPathNodeType.Text:
\r
436 writer.WriteString (navigator.Value);
\r
439 case XPathNodeType.SignificantWhitespace:
\r
440 case XPathNodeType.Whitespace:
\r
441 writer.WriteWhitespace (navigator.Value);
\r
448 #region Calls to external libraries
\r
450 [DllImport ("libxslt")]
\r
451 static extern IntPtr xsltParseStylesheetFile (string filename);
\r
453 [DllImport ("libxslt")]
\r
454 static extern IntPtr xsltParseStylesheetDoc (IntPtr docPtr);
\r
456 [DllImport ("libxslt")]
\r
457 static extern IntPtr xsltApplyStylesheet (IntPtr stylePtr, IntPtr DocPtr, IntPtr notused);
\r
459 [DllImport ("libxslt")]
\r
460 static extern int xsltSaveResultToFilename (string URI, IntPtr doc, IntPtr styleSheet, int compression);
\r
462 [DllImport ("libxslt")]
\r
463 static extern void xsltCleanupGlobals ();
\r
465 [DllImport ("libxslt")]
\r
466 static extern void xsltFreeStylesheet (IntPtr cur);
\r
469 [DllImport ("libxml2")]
\r
470 static extern IntPtr xmlNewDoc (string version);
\r
472 [DllImport ("libxml2")]
\r
473 static extern int xmlSaveFile (string filename, IntPtr cur);
\r
475 [DllImport ("libxml2")]
\r
476 static extern IntPtr xmlParseFile (string filename);
\r
478 [DllImport ("libxml2")]
\r
479 static extern IntPtr xmlParseDoc (string document);
\r
481 [DllImport ("libxml2", EntryPoint="xmlParseDoc")]
\r
482 static extern IntPtr xmlParseDocUTF16 ([MarshalAs(UnmanagedType.LPWStr)] string document);
\r
484 [DllImport ("libxml2")]
\r
485 static extern void xmlFreeDoc (IntPtr doc);
\r
487 [DllImport ("libxml2")]
\r
488 static extern void xmlCleanupParser ();
\r
490 [DllImport ("libxml2")]
\r
491 static extern void xmlDocDumpMemory (IntPtr doc, ref IntPtr mem, ref int size);
\r
493 [DllImport ("libxml2")]
\r
494 static extern void xmlFree (IntPtr data);
\r
498 // This class just makes XmlTextWriter use 'encoding="utf-8"'
\r
499 class UTF8StringWriter : StringWriter
\r
501 static Encoding encoding = new UTF8Encoding (false);
\r
503 public override Encoding Encoding {
\r