// 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
}
}