// Copyright © Microsoft Corporation. // This source file is subject to the Microsoft Permissive License. // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. // All other rights reserved. using System; using System.IO; using System.Xml; using System.Xml.Xsl; using Microsoft.Ddue.Tools.CommandLine; namespace Microsoft.Ddue.Tools { public class XslTransformer { public static int Main(string[] args) { // specify options OptionCollection options = new OptionCollection(); options.Add(new SwitchOption("?", "Show this help page.")); options.Add(new ListOption("xsl", "Sepcify transform files.", "xsltPath")); options.Add(new ListOption("arg", "Sepcify arguments.", "name=value")); options.Add(new StringOption("out", "Specify an output file. If unspecified, output goes to the console.", "outputFilePath")); options.Add(new SwitchOption("w", "Do not ignore insignificant whitespace. By default insignificant whitespace is ignored.")); ConsoleApplication.WriteBanner(); // process options ParseArgumentsResult results = options.ParseArguments(args); if (results.Options["?"].IsPresent) { Console.WriteLine("XslTransformer xsl_file [xml_file] [options]"); options.WriteOptionSummary(Console.Out); return (0); } // check for invalid options if (!results.Success) { results.WriteParseErrors(Console.Out); return (1); } // check for missing or extra assembly directories if (results.UnusedArguments.Count != 1) { Console.WriteLine("Specify one input XML input file."); return (1); } if (!results.Options["xsl"].IsPresent) { Console.WriteLine("Specify at least one XSL transform file."); return (1); } // set whitespace setting bool ignoreWhitespace = !results.Options["w"].IsPresent; // Load transforms string[] transformFiles = (string[])results.Options["xsl"].Value; XslCompiledTransform[] transforms = new XslCompiledTransform[transformFiles.Length]; for (int i = 0; i < transformFiles.Length; i++) { string transformFile = Environment.ExpandEnvironmentVariables(transformFiles[i]); transforms[i] = new XslCompiledTransform(); XsltSettings transformSettings = new XsltSettings(true, true); try { transforms[i].Load(transformFile, transformSettings, new XmlUrlResolver()); } catch (IOException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The transform file '{0}' could not be loaded. The error is: {1}", transformFile, e.Message)); return (1); } catch (UnauthorizedAccessException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The transform file '{0}' could not be loaded. The error is: {1}", transformFile, e.Message)); return (1); } catch (XsltException e) { if (e.InnerException != null) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The transformation file '{0}' is not valid. The error is: {1}", transformFile, e.InnerException.Message)); } else { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The transformation file '{0}' is not valid. The error is: {1}", transformFile, e.Message)); } return (1); } catch (XmlException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The transform file '{0}' is not well-formed. The error is: {1}", transformFile, e.Message)); return (1); } } // Compose the arguments XsltArgumentList arguments = new XsltArgumentList(); if (results.Options["arg"].IsPresent) { string[] nameValueStrings = (string[])results.Options["arg"].Value; foreach (string nameValueString in nameValueStrings) { string[] nameValuePair = nameValueString.Split('='); if (nameValuePair.Length != 2) continue; arguments.AddParam(nameValuePair[0], String.Empty, nameValuePair[1]); } } string input = Environment.ExpandEnvironmentVariables(results.UnusedArguments[0]); // prepare the reader XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.IgnoreWhitespace = ignoreWhitespace; // Do each transform for (int i = 0; i < transforms.Length; i++) { ConsoleApplication.WriteMessage(LogLevel.Info, String.Format("Applying XSL transformation '{0}'.", transformFiles[i])); // get the transform XslCompiledTransform transform = transforms[i]; // figure out where to put the output string output; if (i < (transforms.Length - 1)) { try { output = Path.GetTempFileName(); File.SetAttributes(output, FileAttributes.Temporary); } catch (IOException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("An error occured while attempting to create a temporary file. The error message is: {0}", e.Message)); return (1); } } else { if (results.Options["out"].IsPresent) { output = Environment.ExpandEnvironmentVariables((string)results.Options["out"].Value); } else { output = null; } } // create a reader Stream readStream; try { readStream = File.Open(input, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); } catch (IOException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The input file '{0}' could not be loaded. The error is: {1}", input, e.Message)); return (1); } catch (UnauthorizedAccessException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The input file '{0}' could not be loaded. The error is: {1}", input, e.Message)); return (1); } using (XmlReader reader = XmlReader.Create(readStream, readerSettings)) { // create a writer Stream outputStream; if (output == null) { outputStream = Console.OpenStandardOutput(); } else { try { outputStream = File.Open(output, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete); } catch (IOException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The output file '{0}' could not be loaded. The error is: {1}", output, e.Message)); return (1); } catch (UnauthorizedAccessException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The output file '{0}' could not be loaded. The error is: {1}", output, e.Message)); return (1); } } using (XmlWriter writer = XmlWriter.Create(outputStream, transform.OutputSettings)) { try { // do the deed transform.Transform(reader, arguments, writer); } catch (XsltException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("An error occured during the transformation. The error message is: {0}", (e.InnerException == null) ? e.Message : e.InnerException.Message)); return (1); } catch (XmlException e) { ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("The input file '{0}' is not well-formed. The error is: {1}", input, e.Message)); return (1); } } } // if the last input was a temp file, delete it if (i > 0) { // Console.WriteLine("deleting {0}", input); try { File.Delete(input); } catch (IOException e) { ConsoleApplication.WriteMessage(LogLevel.Warn, String.Format("The temporary file '{0}' could not be deleted. The error message is: {1}", input, e.Message)); } } // the last output file is the next input file input = output; } return (0); } } internal class TransformInfo { public TransformInfo(string file) { this.file = file; transform.Load(file, settings, resolver); } private string file; private XslCompiledTransform transform = new XslCompiledTransform(); public string File { get { return (file); } } public XslCompiledTransform Transform { get { return (transform); } } private static XsltSettings settings = new XsltSettings(true, true); private static XmlUrlResolver resolver = new XmlUrlResolver(); } }