git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@422692 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -239,6 +239,7 @@ Tom Cunningham | |||
| Tom Dimock | |||
| Tom Eugelink | |||
| Ulrich Schmidt | |||
| Victor Toni | |||
| Waldek Herka | |||
| Will Wang | |||
| William Ferguson | |||
| @@ -950,6 +950,10 @@ | |||
| <first>Ulrich</first> | |||
| <last>Schmidt</last> | |||
| </name> | |||
| <name> | |||
| <first>Victor</first> | |||
| <last>Toni</last> | |||
| </name> | |||
| <name> | |||
| <first>Will</first> | |||
| <last>Wang</last> | |||
| @@ -16,7 +16,7 @@ | |||
| or for generating code.</p> | |||
| <p><b>Note:</b> If you are using JDK 1.4 or higher, this task does not require external libraries | |||
| not supplied in the Ant distribution. However, often the built in XSL engine is not as up | |||
| to date as a fresh download, so an update is still highly recommended. | |||
| to date as a fresh download, so an update is still highly recommended. | |||
| See <a href="../install.html#librarydependencies">Library Dependencies</a> for more information.</p> | |||
| <p>It is possible to refine the set of files that are being processed. This can be | |||
| done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>, <i>excludesfile</i> and <i>defaultexcludes</i> | |||
| @@ -189,6 +189,22 @@ element which is used to perform Entity and URI resolution.</p> | |||
| <em>Since Ant 1.7</em>.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">filenameparameter</td> | |||
| <td valign="top">Specifies a xsl parameter for accessing the name | |||
| of the current processed file. If not set, the file name is not | |||
| passed to the transformation. | |||
| <em>Since Ant 1.7</em>.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">filedirparameter</td> | |||
| <td valign="top">Specifies a xsl parameter for accessing the directory | |||
| of the current processed file. If not set, the directory is not | |||
| passed to the transformation. | |||
| <em>Since Ant 1.7</em>.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Parameters specified as nested elements</h3> | |||
| @@ -312,7 +328,7 @@ And in Saxon 7.x: | |||
| <li>http://saxon.sf.net/feature/linenumbering (integer)</li> | |||
| <li>...</li> | |||
| </ul> | |||
| <blockquote> | |||
| <blockquote> | |||
| <h4>Parameters</h4> | |||
| <table width="60%" border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| @@ -417,10 +433,33 @@ See <a href="../CoreTypes/resources.html">resources</a> to see the concrete synt | |||
| <param name="set" expression="value"/> | |||
| </xslt></pre> | |||
| <h4>Print the current processed file name</h4> | |||
| <pre> | |||
| <project> | |||
| <xslt style="printFilename.xsl" destdir="out" basedir="in" extension=".txt" | |||
| filenameparameter="filename" | |||
| filedirparameter="filedir" | |||
| /> | |||
| </project> | |||
| <xsl:stylesheet | |||
| version="1.0" | |||
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||
| <xsl:param name="filename"></xsl:param> | |||
| <xsl:param name="filedir">.</xsl:param> | |||
| <xsl:template match="/"> | |||
| Current file is <xsl:value-of select="$filename"/> in directory <xsl:value-of select="$filedir"/>. | |||
| </xsl:template> | |||
| </xsl:stylesheet> | |||
| </pre> | |||
| </blockquote> | |||
| <hr> | |||
| <p align="center">Copyright © 2000-2006 The Apache Software Foundation. All rights | |||
| Reserved.</p> | |||
| </body> | |||
| </html> | |||
| </html> | |||
| @@ -11,6 +11,7 @@ | |||
| <target name="teardown"> | |||
| <delete dir="${out.dir}" failonerror="false" /> | |||
| </target> | |||
| <target name="testStyleIsSet"> | |||
| <xslt in="data.xml" out="${out.dir}/out.xml"/> | |||
| @@ -128,5 +129,55 @@ | |||
| <param name="set" expression="value"/> | |||
| </xslt> | |||
| </target> | |||
| </project> | |||
| <target name="testFilenameAndFiledirAsParam"> | |||
| <mkdir dir="${out.dir}/xml/dir"/> | |||
| <mkdir dir="${out.dir}/out"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/one.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/two.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/three.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/dir/four.xml"/> | |||
| <xslt style="printFilename.xsl" | |||
| destdir="${out.dir}/out" | |||
| basedir="${out.dir}/xml" | |||
| includes="**/*.xml" | |||
| extension=".txt" | |||
| filenameparameter="filename" | |||
| filedirparameter="filedir" | |||
| /> | |||
| </target> | |||
| <target name="testFilenameAsParam"> | |||
| <mkdir dir="${out.dir}/xml/dir"/> | |||
| <mkdir dir="${out.dir}/out"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/one.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/two.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/three.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/dir/four.xml"/> | |||
| <xslt style="printFilename.xsl" | |||
| destdir="${out.dir}/out" | |||
| basedir="${out.dir}/xml" | |||
| includes="**/*.xml" | |||
| extension=".txt" | |||
| filenameparameter="filename" | |||
| /> | |||
| </target> | |||
| <target name="testFilenameAsParamNoSetting"> | |||
| <mkdir dir="${out.dir}/xml/dir"/> | |||
| <mkdir dir="${out.dir}/out"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/one.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/two.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/three.xml"/> | |||
| <copy file="data.xml" tofile="${out.dir}/xml/dir/four.xml"/> | |||
| <xslt style="printFilename.xsl" | |||
| destdir="${out.dir}/out" | |||
| basedir="${out.dir}/xml" | |||
| includes="**/*.xml" | |||
| extension=".txt" | |||
| /> <!-- without 'filenameparameter' to check, that the xsl:param is NOT set --> | |||
| </target> | |||
| </project> | |||
| @@ -0,0 +1,22 @@ | |||
| <?xml version="1.0"?> | |||
| <xsl:stylesheet | |||
| version="1.0" | |||
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||
| <xsl:output indent="no" method="text"/> | |||
| <xsl:strip-space elements="*"/> | |||
| <xsl:param name="filename">-not-set-</xsl:param> | |||
| <xsl:param name="filedir">-not-set-</xsl:param> | |||
| <!-- use the xsl-parameter --> | |||
| <xsl:template match="/"> | |||
| filename='<xsl:value-of select="$filename"/>' | |||
| filedir ='<xsl:value-of select="$filedir"/>' | |||
| </xsl:template> | |||
| <!-- delete the raw xml data --> | |||
| <xsl:template match="*"/> | |||
| </xsl:stylesheet> | |||
| @@ -63,6 +63,12 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| /** extension of the files produced by XSL processing */ | |||
| private String targetExtension = ".html"; | |||
| /** name for XSL parameter containing the filename */ | |||
| private String fileNameParameter = null; | |||
| /** name for XSL parameter containing the file directory */ | |||
| public String fileDirParameter = null; | |||
| /** additional parameters to be passed to the stylesheets */ | |||
| private Vector params = new Vector(); | |||
| @@ -472,6 +478,28 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog); | |||
| } | |||
| /** | |||
| * Pass the filename of the current processed file as a xsl parameter | |||
| * to the transformation. This value sets the name of that xsl parameter. | |||
| * | |||
| * @param fileNameParameter name of the xsl parameter retrieving the | |||
| * current file name | |||
| */ | |||
| public void setFileNameParameter(String fileNameParameter) { | |||
| this.fileNameParameter = fileNameParameter; | |||
| } | |||
| /** | |||
| * Pass the directory name of the current processed file as a xsl parameter | |||
| * to the transformation. This value sets the name of that xsl parameter. | |||
| * | |||
| * @param fileDirParameter name of the xsl parameter retrieving the | |||
| * current file directory | |||
| */ | |||
| public void setFileDirParameter(String fileDirParameter) { | |||
| this.fileDirParameter = fileDirParameter; | |||
| } | |||
| /** | |||
| * Load processor here instead of in setProcessor - this will be | |||
| * called from within execute, so we have access to the latest | |||
| @@ -623,6 +651,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| log("Processing " + inF + " to " + outF); | |||
| configureLiaison(stylesheet); | |||
| setLiaisonDynamicFileParameters(liaison, inF); | |||
| liaison.transform(inF, outF); | |||
| } | |||
| } catch (Exception ex) { | |||
| @@ -662,6 +691,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| log("Processing " + inFile + " to " + outFile, | |||
| Project.MSG_INFO); | |||
| configureLiaison(stylesheet); | |||
| setLiaisonDynamicFileParameters(liaison, inFile); | |||
| liaison.transform(inFile, outFile); | |||
| } else { | |||
| log("Skipping input file " + inFile | |||
| @@ -721,7 +751,6 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| return outputProperties.elements(); | |||
| } | |||
| /** | |||
| * Get the Liason implementation to use in processing. | |||
| * | |||
| @@ -993,6 +1022,31 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| } | |||
| } | |||
| /** | |||
| * Sets file parameter(s) for directory and filename if the attribute | |||
| * 'filenameparameter' or 'filedirparameter' are set in the task. | |||
| * | |||
| * @param liaison to change parameters for | |||
| * @param inFile to get the additional file information from | |||
| * @throws Exception if an exception occurs on filename lookup | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| private void setLiaisonDynamicFileParameters( | |||
| XSLTLiaison liaison, | |||
| File inFile | |||
| ) throws Exception { | |||
| String fileName = FileUtils.getRelativePath(baseDir, inFile); | |||
| File file = new File(fileName); | |||
| if (fileNameParameter != null) { | |||
| liaison.addParam(fileNameParameter, inFile.getName()); | |||
| } | |||
| if (fileDirParameter != null) { | |||
| liaison.addParam(fileDirParameter, (file.getParent()!=null) ? file.getParent() : "" ); | |||
| } | |||
| } | |||
| /** | |||
| * Create the factory element to configure a trax liaison. | |||
| * @return the newly created factory element. | |||
| @@ -1148,4 +1202,4 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -25,6 +25,7 @@ import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.util.Hashtable; | |||
| import java.util.Vector; | |||
| import java.util.Enumeration; | |||
| import java.net.URL; | |||
| @@ -114,7 +115,7 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
| private Vector outputProperties = new Vector(); | |||
| /** stylesheet parameters */ | |||
| private Vector params = new Vector(); | |||
| private Hashtable params = new Hashtable(); | |||
| /** factory attributes */ | |||
| private Vector attributes = new Vector(); | |||
| @@ -177,6 +178,11 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
| // not sure what could be the need of this... | |||
| res.setSystemId(JAXPUtils.getSystemId(outfile)); | |||
| Source src = getSource(fis, infile); | |||
| // set parameters on each transformation, maybe something has changed | |||
| //(e.g. value of file name parameter) | |||
| setTransformationParameters(); | |||
| transformer.transform(src, res); | |||
| } finally { | |||
| // make sure to close all handles, otherwise the garbage | |||
| @@ -320,16 +326,23 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
| if (uriResolver != null) { | |||
| transformer.setURIResolver(uriResolver); | |||
| } | |||
| for (int i = 0; i < params.size(); i++) { | |||
| final String[] pair = (String[]) params.elementAt(i); | |||
| transformer.setParameter(pair[0], pair[1]); | |||
| } | |||
| for (int i = 0; i < outputProperties.size(); i++) { | |||
| final String[] pair = (String[]) outputProperties.elementAt(i); | |||
| transformer.setOutputProperty(pair[0], pair[1]); | |||
| } | |||
| } | |||
| /** | |||
| * Sets the paramters for the transformer. | |||
| */ | |||
| private void setTransformationParameters() { | |||
| for (final Enumeration enumeration = params.keys(); enumeration.hasMoreElements(); ) { | |||
| final String name = (String) enumeration.nextElement(); | |||
| final String value = (String) params.get(name); | |||
| transformer.setParameter(name, value); | |||
| } | |||
| } | |||
| /** | |||
| * return the Transformer factory associated to this liaison. | |||
| * @return the Transformer factory associated to this liaison. | |||
| @@ -426,8 +439,7 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
| * @param value the value of the parameter | |||
| */ | |||
| public void addParam(String name, String value) { | |||
| final String[] pair = new String[]{name, value}; | |||
| params.addElement(pair); | |||
| params.put(name, value); | |||
| } | |||
| /** | |||
| @@ -552,4 +564,4 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
| setOutputProperty(prop.getName(), prop.getValue()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -27,6 +27,10 @@ import java.io.OutputStream; | |||
| import java.net.MalformedURLException; | |||
| import java.net.URL; | |||
| import java.text.DecimalFormat; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.Iterator; | |||
| import java.util.List; | |||
| import java.util.Random; | |||
| import java.util.Stack; | |||
| import java.util.StringTokenizer; | |||
| @@ -85,7 +89,7 @@ public class FileUtils { | |||
| * | |||
| * @return a new instance of FileUtils. | |||
| * @deprecated since 1.7. | |||
| * Use getFileUtils instead, | |||
| * Use getFileUtils instead, | |||
| * FileUtils do not have state. | |||
| */ | |||
| public static FileUtils newFileUtils() { | |||
| @@ -617,7 +621,7 @@ public class FileUtils { | |||
| * | |||
| * @return the native version of the specified path or | |||
| * an empty string if the path is <code>null</code> or empty. | |||
| * | |||
| * | |||
| * @since ant 1.7 | |||
| * @see PathTokenizer | |||
| */ | |||
| @@ -1342,5 +1346,139 @@ public class FileUtils { | |||
| file.delete(); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Calculates the relative path between to files. | |||
| * <p> | |||
| * Implementation note:<br/> This function my throw an IOException if an | |||
| * I/O error occurs because its use of the canonical pathname may require | |||
| * filesystem queries. | |||
| * </p> | |||
| * | |||
| * @param fromFile | |||
| * the <code>File</code> to calculate the path from | |||
| * @param toFile | |||
| * the <code>File</code> to calculate the path to | |||
| * @return | |||
| * @throws Exception | |||
| * @see {@link File#getCanonicalPath()} | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public static String getRelativePath( | |||
| File fromFile, | |||
| File toFile | |||
| ) throws Exception { | |||
| String fromPath = fromFile.getCanonicalPath(); | |||
| String toPath = toFile.getCanonicalPath(); | |||
| // build the path stack info to compare | |||
| String[] fromPathStack = getPathStack(fromPath); | |||
| String[] toPathStack = getPathStack(toPath); | |||
| if (0 < toPathStack.length && 0 < fromPathStack.length) { | |||
| if (!fromPathStack[0].equals(toPathStack[0])) { | |||
| // not the same device (would be "" on Linux/Unix) | |||
| return getPath(Arrays.asList(toPathStack)); | |||
| } | |||
| } else { | |||
| // no comparison possible | |||
| return getPath(Arrays.asList(toPathStack)); | |||
| } | |||
| int minLength = Math | |||
| .min(fromPathStack.length, toPathStack.length); | |||
| int same = 1; | |||
| // get index of parts which are equal | |||
| for (; same < minLength; same++) { | |||
| if (!fromPathStack[same].equals(toPathStack[same])) { | |||
| break; | |||
| } | |||
| } | |||
| List relativePathStack = new ArrayList(); | |||
| // if "from" part is longer, fill it up with ".." | |||
| // to reach path which is equal to both paths | |||
| for (int i = same; i < fromPathStack.length; i++) { | |||
| relativePathStack.add(".."); | |||
| } | |||
| // fill it up path with parts which were not equal | |||
| for (int i = same; i < toPathStack.length; i++) { | |||
| relativePathStack.add(toPathStack[i]); | |||
| } | |||
| return getPath(relativePathStack); | |||
| } | |||
| /** | |||
| * Gets all names of the path as an array of <code>String</code>s. | |||
| * | |||
| * @param path | |||
| * to get names from | |||
| * @return <code>String</code>s, never <code>null</code> | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public static String[] getPathStack(String path) { | |||
| String normalizedPath = path.replace(File.separatorChar, '/'); | |||
| // since Java 1.4 | |||
| //return normalizedPath.split("/"); | |||
| // workaround for Java 1.2-1.3 | |||
| Object[] tokens = StringUtils.split(normalizedPath, '/').toArray(); | |||
| String[] rv = new String[tokens.length]; | |||
| System.arraycopy(tokens, 0, rv, 0, tokens.length); | |||
| return rv; | |||
| } | |||
| /** | |||
| * Gets path from a <code>List</code> of <code>String</code>s. | |||
| * | |||
| * @param pathStack | |||
| * <code>List</code> of <code>String</code>s to be concated | |||
| * as a path. | |||
| * @return <code>String</code>, never <code>null</code> | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public static String getPath(List pathStack) { | |||
| // can safely use '/' because Windows understands '/' as separator | |||
| return getPath(pathStack, '/'); | |||
| } | |||
| /** | |||
| * Gets path from a <code>List</code> of <code>String</code>s. | |||
| * | |||
| * @param pathStack | |||
| * <code>List</code> of <code>String</code>s to be concated | |||
| * as a path. | |||
| * @param separatorChar | |||
| * <code>char</code> to be used as separator between names in | |||
| * path | |||
| * @return <code>String</code>, never <code>null</code> | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public static String getPath(final List pathStack, final char separatorChar) { | |||
| final StringBuffer buffer = new StringBuffer(); | |||
| final Iterator iter = pathStack.iterator(); | |||
| if (iter.hasNext()) { | |||
| buffer.append(iter.next()); | |||
| } | |||
| while (iter.hasNext()) { | |||
| buffer.append(separatorChar); | |||
| buffer.append(iter.next()); | |||
| } | |||
| return buffer.toString(); | |||
| } | |||
| } | |||
| @@ -86,7 +86,6 @@ public class StyleTest extends BuildFileTest { | |||
| "new-value"); | |||
| } | |||
| public void testDefaultMapper() throws Exception { | |||
| testDefaultMapper("testDefaultMapper"); | |||
| } | |||
| @@ -149,6 +148,32 @@ public class StyleTest extends BuildFileTest { | |||
| expectFileContains("testWithUrlResource", "out/out.xml", "set='value'"); | |||
| } | |||
| public void testFilenameAsParam() throws Exception { | |||
| executeTarget("testFilenameAsParam"); | |||
| assertFileContains("out/out/one.txt", "filename='one.xml'"); | |||
| assertFileContains("out/out/two.txt", "filename='two.xml'"); | |||
| assertFileContains("out/out/three.txt", "filename='three.xml'"); | |||
| assertFileContains("out/out/dir/four.txt", "filename='four.xml'"); | |||
| assertFileContains("out/out/dir/four.txt", "filedir ='-not-set-'"); | |||
| } | |||
| public void testFilenameAsParamNoSetting() throws Exception { | |||
| executeTarget("testFilenameAsParamNoSetting"); | |||
| assertFileContains("out/out/one.txt", "filename='-not-set-'"); | |||
| assertFileContains("out/out/two.txt", "filename='-not-set-'"); | |||
| assertFileContains("out/out/three.txt", "filename='-not-set-'"); | |||
| assertFileContains("out/out/dir/four.txt", "filename='-not-set-'"); | |||
| } | |||
| public void testFilenameAndFiledirAsParam() throws Exception { | |||
| executeTarget("testFilenameAndFiledirAsParam"); | |||
| assertFileContains("out/out/one.txt", "filename='one.xml'"); | |||
| assertFileContains("out/out/one.txt", "filedir =''"); | |||
| assertFileContains("out/out/dir/four.txt", "filename='four.xml'"); | |||
| assertFileContains("out/out/dir/four.txt", "filedir ='dir'"); | |||
| } | |||
| // ************* copied from ConcatTest ************* | |||
| // ------------------------------------------------------ | |||
| @@ -166,25 +191,23 @@ public class StyleTest extends BuildFileTest { | |||
| finally { | |||
| FileUtils.close(r); | |||
| } | |||
| } | |||
| private String getFileString(String target, String filename) | |||
| private void expectFileContains( | |||
| String target, String filename, String contains) | |||
| throws IOException | |||
| { | |||
| executeTarget(target); | |||
| return getFileString(filename); | |||
| assertFileContains(filename, contains); | |||
| } | |||
| private void expectFileContains( | |||
| String target, String filename, String contains) | |||
| throws IOException | |||
| { | |||
| String content = getFileString(target, filename); | |||
| private void assertFileContains(String filename, String contains) throws IOException { | |||
| String content = getFileString(filename); | |||
| assertTrue( | |||
| "expecting file " + filename + " to contain " + | |||
| contains + | |||
| " but got " + content, content.indexOf(contains) > -1); | |||
| "expecting file " + filename | |||
| + " to contain " + contains | |||
| + " but got " + content, | |||
| content.indexOf(contains) > -1); | |||
| } | |||
| } | |||
| } | |||