// NAnt - A .NET build tool // Copyright (C) 2001 Gerry Shaw // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // Serge (serge@wildwestsoftware.com) // Gerry Shaw (gerry_shaw@yahoo.com) // Scott Hernandez (ScottHernandez@hotmail.com) // Tim Noll (tim.noll@gmail.com) // Giuseppe Greco (giuseppe.greco@agamura.com) using System; using System.Collections; using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Net; using System.Xml; using System.Xml.Xsl; using System.Xml.XPath; using NAnt.Core.Attributes; using NAnt.Core.Types; using NAnt.Core.Util; namespace NAnt.Core.Tasks { /// /// Processes a document via XSLT. /// /// /// Create a report in HTML. /// /// /// ]]> /// /// /// /// Create a report in HTML, with a param. /// /// /// /// /// /// /// ]]> /// /// /// /// Create a report in HTML, with a expanded param. /// /// /// /// /// /// /// ]]> /// /// /// /// Create some code based on a directory of templates. /// /// /// /// /// /// /// /// /// /// ]]> /// /// [TaskName("style")] public class StyleTask : Task { #region Private Instance Fields private DirectoryInfo _destDir; private string _extension = "html"; private Uri _xsltFile; private FileInfo _srcFile; private FileInfo _outputFile; private FileSet _inFiles = new FileSet(); private XsltParameterCollection _xsltParameters = new XsltParameterCollection(); private XsltExtensionObjectCollection _xsltExtensions = new XsltExtensionObjectCollection(); private Proxy _proxy; #endregion Private Instance Fields #region Public Instance Properties /// /// Directory in which to store the results. The default is the project /// base directory. /// [TaskAttribute("destdir", Required=false)] public DirectoryInfo DestDir { get { if (_destDir == null) { return new DirectoryInfo(Project.BaseDirectory); } return _destDir; } set { _destDir = value; } } /// /// Desired file extension to be used for the targets. The default is /// html. /// [TaskAttribute("extension", Required=false)] public string Extension { get { return _extension; } set { _extension = value; } } /// /// URI or path that points to the stylesheet to use. If given as path, it can /// be relative to the project's basedir or absolute. /// [TaskAttribute("style", Required=true)] public Uri XsltFile { get { return _xsltFile; } set { _xsltFile = value; } } /// /// Specifies a single XML document to be styled. Should be used with /// the attribute. /// [TaskAttribute("in", Required=false)] public FileInfo SrcFile { get { return _srcFile; } set { _srcFile = value; } } /// /// Specifies the output name for the styled result from the /// attribute. /// [TaskAttribute("out", Required=false)] public FileInfo OutputFile { get { return _outputFile; } set { _outputFile = value; } } /// /// Specifies a group of input files to which to apply the stylesheet. /// [BuildElement("infiles")] public FileSet InFiles { get { return _inFiles; } set { _inFiles = value; } } /// /// XSLT parameters to be passed to the XSLT transformation. /// [BuildElementCollection("parameters", "parameter")] public XsltParameterCollection Parameters { get { return _xsltParameters; } } /// /// XSLT extension objects to be passed to the XSLT transformation. /// [BuildElementCollection("extensionobjects", "extensionobject")] public XsltExtensionObjectCollection ExtensionObjects { get { return _xsltExtensions; } } /// /// The network proxy to use to access the Internet resource. /// [BuildElement("proxy")] public Proxy Proxy { get { return _proxy; } set { _proxy = value; } } #endregion Public Instance Properties #region Override implementation of Task protected override void InitializeTask(XmlNode taskNode) { // deprecated as of NAnt 0.8.4 // TO-DO : remove this after NAnt 0.8.5 or so // Load parameters foreach (XmlNode node in taskNode) { if (node.LocalName.Equals("param")) { Log(Level.Warning, "The usage of the element is" + " deprecated. Please use the collection" + " instead."); // create and fill XsltParameter XsltParameter xsltParameter = new XsltParameter(); xsltParameter.ParameterName = Project.ExpandProperties(node.Attributes["name"].Value, Location); xsltParameter.Value = Project.ExpandProperties(node.Attributes["expression"].Value, Location); // add parameter to collection _xsltParameters.Add(xsltParameter); } } } protected override void ExecuteTask() { // ensure base directory is set, even if fileset was not initialized // from XML if (InFiles.BaseDirectory == null) { InFiles.BaseDirectory = new DirectoryInfo(Project.BaseDirectory); } StringCollection srcFiles = null; if (SrcFile != null) { srcFiles = new StringCollection(); srcFiles.Add(SrcFile.FullName); } else if (InFiles.FileNames.Count > 0) { if (OutputFile != null) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1148")), Location); } srcFiles = InFiles.FileNames; } if (srcFiles == null || srcFiles.Count == 0) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1147")), Location); } if (XsltFile.IsFile) { FileInfo fileInfo = new FileInfo(XsltFile.LocalPath); if (!fileInfo.Exists) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1149"), fileInfo.FullName), Location); } } else { HttpWebRequest request = (HttpWebRequest) WebRequest.Create(XsltFile); if (Proxy != null) { request.Proxy = Proxy.GetWebProxy(); } HttpWebResponse response = (HttpWebResponse) request.GetResponse(); if (response.StatusCode != HttpStatusCode.OK) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1149"), XsltFile), Location); } } foreach (string srcFile in srcFiles) { string destFile = null; if (OutputFile != null) { destFile = OutputFile.FullName; } if (StringUtils.IsNullOrEmpty(destFile)) { // TODO: use System.IO.Path (gs) // append extension if necessary string ext = Extension.IndexOf(".") > -1 ? Extension : "." + Extension; int extPos = srcFile.LastIndexOf('.'); if (extPos == -1) { destFile = srcFile + ext; } else { destFile = srcFile.Substring(0, extPos) + ext; } destFile = Path.GetFileName(destFile); } FileInfo srcInfo = new FileInfo(srcFile); FileInfo destInfo = new FileInfo(Path.GetFullPath(Path.Combine( DestDir.FullName, destFile))); if (!srcInfo.Exists) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1150"), srcInfo.FullName), Location); } bool destOutdated = !destInfo.Exists || srcInfo.LastWriteTime > destInfo.LastWriteTime; if (!destOutdated && XsltFile.IsFile) { FileInfo fileInfo = new FileInfo(XsltFile.LocalPath); destOutdated |= fileInfo.LastWriteTime > destInfo.LastWriteTime; } if (destOutdated) { XmlReader xmlReader = null; XmlReader xslReader = null; TextWriter writer = null; try { // store current directory string originalCurrentDirectory = Directory.GetCurrentDirectory(); // initialize XPath document holding input XML XPathDocument xml = null; try { // change current directory to directory containing // XSLT file, to allow includes to be resolved // correctly Directory.SetCurrentDirectory(srcInfo.DirectoryName); // load the xml that needs to be transformed Log(Level.Verbose, "Loading XML file '{0}'.", srcInfo.FullName); xmlReader = CreateXmlReader(new Uri(srcInfo.FullName)); xml = new XPathDocument(xmlReader); } finally { // restore original current directory Directory.SetCurrentDirectory(originalCurrentDirectory); } // initialize xslt parameters XsltArgumentList xsltArgs = new XsltArgumentList(); // set the xslt parameters foreach (XsltParameter parameter in Parameters) { if (IfDefined && !UnlessDefined) { xsltArgs.AddParam(parameter.ParameterName, parameter.NamespaceUri, parameter.Value); } } // create extension objects foreach (XsltExtensionObject extensionObject in ExtensionObjects) { if (extensionObject.IfDefined && !extensionObject.UnlessDefined) { object extensionInstance = extensionObject.CreateInstance(); xsltArgs.AddExtensionObject(extensionObject.NamespaceUri, extensionInstance); } } // initialize XSLT transform XslTransform xslt = new XslTransform(); try { if (XsltFile.IsFile) { // change current directory to directory containing // XSLT file, to allow includes to be resolved // correctly FileInfo fileInfo = new FileInfo(XsltFile.LocalPath); Directory.SetCurrentDirectory(fileInfo.DirectoryName); } // load the stylesheet Log(Level.Verbose, "Loading stylesheet '{0}'.", XsltFile); xslReader = CreateXmlReader(XsltFile); xslt.Load(xslReader); // create writer for the destination xml writer = CreateWriter(destInfo.FullName); // do the actual transformation Log(Level.Info, "Processing '{0}' to '{1}'.", srcInfo.FullName, destInfo.FullName); xslt.Transform(xml, xsltArgs, writer); } finally { // restore original current directory Directory.SetCurrentDirectory(originalCurrentDirectory); } } catch (Exception ex) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1151"), srcInfo.FullName, XsltFile), Location, ex); } finally { // ensure file handles are closed if (xmlReader != null) { xmlReader.Close(); } if (xslReader != null) { xslReader.Close(); } if (writer != null) { writer.Close(); } } } } } #endregion Override implementation of Task #region Protected Instance Methods protected virtual XmlReader CreateXmlReader(Uri uri) { Stream stream = null; XmlUrlResolver resolver = null; if (uri.IsFile) { stream = new FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read); } else { resolver = new XmlUrlResolver(); HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(uri); if (Proxy != null) { request.Proxy = Proxy.GetWebProxy(); resolver.Credentials = Proxy.Credentials.GetCredential(); } else { resolver.Credentials = CredentialCache.DefaultCredentials; } stream = request.GetResponse().GetResponseStream(); } XmlTextReader xmlReader = new XmlTextReader(uri.ToString(), stream); xmlReader.XmlResolver = resolver; return new XmlValidatingReader(xmlReader); } protected virtual TextWriter CreateWriter(string filepath) { string targetDir = Path.GetDirectoryName(Path.GetFullPath(filepath)); if (!StringUtils.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } return new StreamWriter(filepath); } #endregion Protected Instance Methods } }