git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1548237 13f79535-47bb-0310-9956-ffa450edef68master
@@ -121,6 +121,7 @@ Ernst de Haan | |||
Frank Harnack | |||
Frank Somers | |||
Frank Zeyda | |||
Frantisek Kucera | |||
Frederic Bothamy | |||
Frederic Lavigne | |||
Gary S. Weaver | |||
@@ -32,6 +32,9 @@ Other changes: | |||
* <filterset> now supports a nested <propertyset> to specify filters. | |||
Bugzilla Report 55794. | |||
* <xslt>'s params can now be typed. | |||
Bugzilla Report 21525. | |||
Changes from Ant 1.9.1 TO Ant 1.9.2 | |||
=================================== | |||
@@ -507,6 +507,10 @@ | |||
<first>Frank</first> | |||
<last>Zeyda</last> | |||
</name> | |||
<name> | |||
<first>František</first> | |||
<last>Kučera</last> | |||
</name> | |||
<name> | |||
<first>Frédéric</first> | |||
<last>Bothamy</last> | |||
@@ -295,10 +295,32 @@ element is used to perform Entity and URI resolution.</p> | |||
</tr> | |||
<tr> | |||
<td valign="top">expression</td> | |||
<td valign="top">Text value to be placed into the param.<br> | |||
Was originally intended to be an XSL expression.</td> | |||
<td valign="top"> | |||
The value to be placed into the param or an XPath expression | |||
(depending on <code>type</code>). | |||
</td> | |||
<td align="center" valign="top">Yes</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">type</td> | |||
<td valign="top"> | |||
Data type of the parameter. Possible values are: | |||
<ul> | |||
<li><code>STRING</code></li> | |||
<li><code>BOOLEAN</code></li> | |||
<li><code>INT</code></li> | |||
<li><code>LONG</code></li> | |||
<li><code>DOUBLE</code></li> | |||
<li><code>XPATH_STRING</code></li> | |||
<li><code>XPATH_BOOLEAN</code></li> | |||
<li><code>XPATH_NUMBER</code></li> | |||
<li><code>XPATH_NODE</code></li> | |||
<li><code>XPATH_NODESET</code></li> | |||
</ul> | |||
<em>since Ant 1.9.3</em> | |||
</td> | |||
<td align="center" valign="top">No; default is <code>STRING</code></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">if</td> | |||
<td valign="top">The param will only be passed <a href="../properties.html#if+unless">if this property is set</a>.</td> | |||
@@ -313,6 +335,18 @@ element is used to perform Entity and URI resolution.</p> | |||
</table> | |||
</blockquote> | |||
<p> | |||
The <code>XPATH_*</code> types says that the <code>expression</code> is not just a primitive-type value but an XPath expression. | |||
This expression will be evaluated on an empty XML document and the result will be passed to the XSLT transformer as a parameter of given type. | |||
In these expressions the declared Ant properties can be used as XPath variables e.g. <code>$someProperty</code>. | |||
So you can compute something using standard XPath functions and operators. | |||
</p> | |||
<p> | |||
If you write <code>${someProperty}</code> instead of <code>$someProperty</code>, | |||
the value will be simply substituted by Ant before evaluating the XPath expression | |||
(this substitution works also for primitive types). | |||
</p> | |||
<h4>outputproperty ('trax' processors only)</h4> | |||
<p>Used to specify how you wish the result tree to be output | |||
as specified in the <a href="http://www.w3.org/TR/xslt#output"> | |||
@@ -459,6 +493,7 @@ with <a href="../Types/propertyset.html">syspropertyset</a>s.</p> | |||
</xslt> | |||
</pre> | |||
<h4>Using XSL parameters</h4> | |||
<p>Simple String parameter:</p> | |||
<pre> | |||
<xslt basedir="doc" destdir="build/doc" | |||
extension=".html" style="style/apache.xsl"> | |||
@@ -469,6 +504,55 @@ with <a href="../Types/propertyset.html">syspropertyset</a>s.</p> | |||
element <xsl:param name="date"/>, the variable | |||
<code>$date</code> will subsequently have the value 07-01-2000. | |||
</p> | |||
<p>Various data types and XPath expressions:</p> | |||
<pre><property name="antProperty1" value="ANT_PROPERTY_1"/> | |||
<property name="antProperty2" value="ANT_PROPERTY_2"/> | |||
<property name="antProperty3" value="3"/> | |||
<property name="antProperty4" value="substring-before"/> | |||
<!-- | |||
${this} is substituted by Ant itself | |||
and $this is evaluated by XPath as a variable | |||
--> | |||
<xslt in="in.xml" out="out.xml" style="template.xsl"> | |||
<!-- Simple String parameter: --> | |||
<param name="p0" expression="some nice string" type="STRING"/> | |||
<!-- A value substituted by Ant --> | |||
<param name="p1" expression="some string with ${antProperty1} constructed by Ant" type="STRING"/> | |||
<!-- XPath resulting in: and this is done in XPath: ANT_PROPERTY_2 --> | |||
<param name="p2" expression="concat('and this is done in XPath: ', $antProperty2)" type="XPATH_STRING"/> | |||
<!-- Some XPath math, result: 42 --> | |||
<param name="p3" expression="64 * 64 div 128 + 10" type="XPATH_NUMBER"/> | |||
<!-- Some numeric parameter: --> | |||
<param name="p4" expression="123.45" type="DOUBLE"/> | |||
<!-- XPath expression, result: true boolean --> | |||
<param name="p5" expression="$antProperty1 = 'ANT_PROPERTY_1'" type="XPATH_BOOLEAN"/> | |||
<!-- First one is an XPath variable, second one is a text substituted by Ant, result: true boolean --> | |||
<param name="p6" expression="$antProperty2 = '${antProperty2}'" type="XPATH_BOOLEAN"/> | |||
<!-- Some XPath math with a variable, result: 64 --> | |||
<param name="p7" expression="$antProperty3 * 4 * 5 + 4" type="XPATH_NUMBER"/> | |||
<!-- | |||
XPath expression with substituted function name and a variable: | |||
substring-before($antProperty2, '_') | |||
result: ANT | |||
--> | |||
<param name="p8" expression="${antProperty4}($antProperty2, '_')" type="XPATH_STRING"/> | |||
<!-- Without type attribute: --> | |||
<param name="p9" expression="default type is String"/> | |||
</xslt></pre> | |||
<h4>Using output properties</h4> | |||
<pre> | |||
@@ -51,6 +51,7 @@ public interface XSLTLiaison { | |||
* @param name the parameter name. | |||
* @param expression the parameter value as an expression string. | |||
* @throws Exception thrown if any problems happens. | |||
* @see XSLTLiaison4#addParam(java.lang.String, java.lang.Object) | |||
* @since Ant 1.3 | |||
*/ | |||
void addParam(String name, String expression) throws Exception; | |||
@@ -0,0 +1,41 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* | |||
*/ | |||
package org.apache.tools.ant.taskdefs; | |||
/** | |||
* Extends Proxy interface for XSLT processors: adds support for XSLT parameters | |||
* of various types (not only String) | |||
* | |||
* | |||
* @see XSLTProcess | |||
* @author Frantisek Kucera (xkucf03) | |||
* @since Ant 1.9.3 | |||
*/ | |||
public interface XSLTLiaison4 extends XSLTLiaison3 { | |||
/** | |||
* Add a parameter to be set during the XSL transformation. | |||
* | |||
* @param name the parameter name. | |||
* @param value the parameter value as String, Boolean, int, etc. | |||
* @throws Exception thrown if any problems happens. | |||
* @since Ant 1.9.3 | |||
* @see Transformer#setParameter(java.lang.String, java.lang.Object) | |||
*/ | |||
void addParam(String name, Object value) throws Exception; | |||
} |
@@ -18,8 +18,20 @@ | |||
package org.apache.tools.ant.taskdefs; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.EnumMap; | |||
import java.util.Enumeration; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Vector; | |||
import javax.xml.namespace.QName; | |||
import javax.xml.xpath.XPath; | |||
import javax.xml.xpath.XPathConstants; | |||
import javax.xml.xpath.XPathExpression; | |||
import javax.xml.xpath.XPathExpressionException; | |||
import javax.xml.xpath.XPathFactory; | |||
import javax.xml.xpath.XPathVariableResolver; | |||
import org.apache.tools.ant.AntClassLoader; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.DirectoryScanner; | |||
@@ -76,7 +88,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
private String fileDirParameter = null; | |||
/** additional parameters to be passed to the stylesheets */ | |||
private Vector params = new Vector(); | |||
private List<Param> params = new ArrayList<Param>(); | |||
/** Input XML document to be used */ | |||
private File inFile = null; | |||
@@ -196,6 +208,19 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
* @since Ant 1.8.0 | |||
*/ | |||
private boolean failOnNoResources = true; | |||
/** | |||
* For evaluating template params | |||
* | |||
* @since Ant 1.9.3 | |||
*/ | |||
private XPathFactory xpathFactory; | |||
/** | |||
* For evaluating template params | |||
* | |||
* @since Ant 1.9.3 | |||
*/ | |||
private XPath xpath; | |||
/** | |||
* System properties to set during transformation. | |||
@@ -305,8 +330,9 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
* Executes the task. | |||
* | |||
* @exception BuildException if there is an execution problem. | |||
* @todo validate that if either in or our is defined, then both are | |||
* @todo validate that if either in or out is defined, then both are | |||
*/ | |||
@Override | |||
public void execute() throws BuildException { | |||
if ("style".equals(getTaskType())) { | |||
log("Warning: the task name <style> is deprecated. Use <xslt> instead.", | |||
@@ -937,7 +963,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
*/ | |||
public Param createParam() { | |||
Param p = new Param(); | |||
params.addElement(p); | |||
params.add(p); | |||
return p; | |||
} | |||
@@ -950,6 +976,12 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
/** The parameter's value */ | |||
private String expression = null; | |||
/** | |||
* Type of the expression. | |||
* @see ParamType | |||
*/ | |||
private String type; | |||
private Object ifCond; | |||
private Object unlessCond; | |||
@@ -974,14 +1006,23 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
} | |||
/** | |||
* The parameter value | |||
* NOTE : was intended to be an XSL expression. | |||
* @param expression the parameter's value. | |||
* The parameter value - | |||
* can be a primitive type value or an XPath expression. | |||
* @param expression the parameter's value/expression. | |||
* @see #setType(java.lang.String) | |||
*/ | |||
public void setExpression(String expression) { | |||
this.expression = expression; | |||
} | |||
/** | |||
* @see ParamType | |||
* @since Ant 1.9.3 | |||
*/ | |||
public void setType(String type) { | |||
this.type = type; | |||
} | |||
/** | |||
* Get the parameter name | |||
* | |||
@@ -1000,6 +1041,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
* | |||
* @return the parameter value | |||
* @exception BuildException if the value is not set. | |||
* @see #getType() | |||
*/ | |||
public String getExpression() throws BuildException { | |||
if (expression == null) { | |||
@@ -1008,6 +1050,14 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
return expression; | |||
} | |||
/** | |||
* @see ParamType | |||
* @since Ant 1.9.3 | |||
*/ | |||
public String getType() { | |||
return type; | |||
} | |||
/** | |||
* Set whether this param should be used. It will be used if | |||
* the expression evaluates to true or the name of a property | |||
@@ -1061,6 +1111,57 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
&& ph.testUnlessCondition(unlessCond); | |||
} | |||
} // Param | |||
/** | |||
* Enum for types of the parameter expression. | |||
* | |||
* <p>The expression can be:</p> | |||
* <ul> | |||
* <li>primitive type that will be parsed from the string value e.g. | |||
* {@linkplain Integer#parseInt(java.lang.String)}</li> | |||
* <li>XPath expression that will be evaluated (outside of the transformed | |||
* document - on empty one) and casted to given type. Inside XPath | |||
* expressions the Ant variables (properties) can be used (as XPath | |||
* variables - e.g. $variable123). n.b. placeholders in form of | |||
* ${variable123} will be substituted with their values before evaluating the | |||
* XPath expression (so it can be used for dynamic XPath function names and | |||
* other hacks).</li> | |||
* </ul> | |||
* <p>The parameter will be then passed to the XSLT template.</p> | |||
* | |||
* <p>Default type (if omited) is primitive String. So if the expression is e.g | |||
* "true" with no type, in XSLT it will be only a text string, not true | |||
* boolean.</p> | |||
* | |||
* @see Param#setType(java.lang.String) | |||
* @see Param#setExpression(java.lang.String) | |||
* @since Ant 1.9.3 | |||
*/ | |||
public enum ParamType { | |||
STRING, | |||
BOOLEAN, | |||
INT, | |||
LONG, | |||
DOUBLE, | |||
XPATH_STRING, | |||
XPATH_BOOLEAN, | |||
XPATH_NUMBER, | |||
XPATH_NODE, | |||
XPATH_NODESET; | |||
public static final Map<ParamType, QName> XPATH_TYPES; | |||
static { | |||
Map<ParamType, QName> m = new EnumMap<ParamType, QName>(ParamType.class); | |||
m.put(XPATH_STRING, XPathConstants.STRING); | |||
m.put(XPATH_BOOLEAN, XPathConstants.BOOLEAN); | |||
m.put(XPATH_NUMBER, XPathConstants.NUMBER); | |||
m.put(XPATH_NODE, XPathConstants.NODE); | |||
m.put(XPATH_NODESET, XPathConstants.NODESET); | |||
XPATH_TYPES = Collections.unmodifiableMap(m); | |||
} | |||
} | |||
/** | |||
* Create an instance of an output property to be configured. | |||
@@ -1119,12 +1220,22 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
} | |||
/** | |||
* Initialize internal instance of XMLCatalog | |||
* Initialize internal instance of XMLCatalog. | |||
* Initialize XPath for parameter evaluation. | |||
* @throws BuildException on error | |||
*/ | |||
@Override | |||
public void init() throws BuildException { | |||
super.init(); | |||
xmlCatalog.setProject(getProject()); | |||
xpathFactory = XPathFactory.newInstance(); | |||
xpath = xpathFactory.newXPath(); | |||
xpath.setXPathVariableResolver(new XPathVariableResolver() { | |||
public Object resolveVariable(QName variableName) { | |||
return getProject().getProperty(variableName.toString()); | |||
} | |||
}); | |||
} | |||
/** | |||
@@ -1179,10 +1290,21 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
return; | |||
} | |||
} | |||
for (Enumeration e = params.elements(); e.hasMoreElements();) { | |||
Param p = (Param) e.nextElement(); | |||
for (Param p : params) { | |||
if (p.shouldUse()) { | |||
liaison.addParam(p.getName(), p.getExpression()); | |||
Object evaluatedParam = evaluateParam(p); | |||
if (liaison instanceof XSLTLiaison4) { | |||
((XSLTLiaison4)liaison).addParam(p.getName(), evaluatedParam); | |||
} else { | |||
if (evaluatedParam == null || evaluatedParam instanceof String) { | |||
liaison.addParam(p.getName(), (String)evaluatedParam); | |||
} else { | |||
log("XSLTLiaison '" + liaison.getClass().getName() | |||
+ "' supports only String parameters. Converting parameter '" + p.getName() | |||
+ "' to its String value '" + evaluatedParam, Project.MSG_WARN); | |||
liaison.addParam(p.getName(), String.valueOf(evaluatedParam)); | |||
} | |||
} | |||
} | |||
} | |||
} catch (Exception ex) { | |||
@@ -1190,6 +1312,56 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger { | |||
handleTransformationError(ex); | |||
} | |||
} | |||
/** | |||
* Evaluates parameter expression according to its type. | |||
* | |||
* @param param parameter from Ant build file | |||
* @return value to be passed to XSLT as parameter | |||
* @throws IllegalArgumentException if param type is unsupported | |||
* @throws NumberFormatException if expression of numeric type is not | |||
* desired numeric type | |||
* @throws XPathExpressionException if XPath expression can not be compiled | |||
* @since Ant 1.9.3 | |||
*/ | |||
private Object evaluateParam(Param param) throws XPathExpressionException { | |||
String typeName = param.getType(); | |||
String expression = param.getExpression(); | |||
ParamType type; | |||
if (typeName == null || typeName.isEmpty()) { | |||
type = ParamType.STRING; // String is default | |||
} else { | |||
try { | |||
type = ParamType.valueOf(typeName); | |||
} catch (IllegalArgumentException e) { | |||
throw new IllegalArgumentException("Invalid XSLT parameter type: " + typeName, e); | |||
} | |||
} | |||
switch (type) { | |||
case STRING: | |||
return expression; | |||
case BOOLEAN: | |||
return Boolean.parseBoolean(expression); | |||
case DOUBLE: | |||
return Double.parseDouble(expression); | |||
case INT: | |||
return Integer.parseInt(expression); | |||
case LONG: | |||
return Long.parseLong(expression); | |||
default: // XPath expression | |||
QName xpathType = ParamType.XPATH_TYPES.get(type); | |||
if (xpathType == null) { | |||
throw new IllegalArgumentException("Invalid XSLT parameter type: " + typeName); | |||
} else { | |||
XPathExpression xpe = xpath.compile(expression); | |||
// null = evaluate XPath on empty XML document | |||
return xpe.evaluate((Object) null, xpathType); | |||
} | |||
} | |||
} | |||
/** | |||
* Sets file parameter(s) for directory and filename if the attribute | |||
@@ -47,7 +47,7 @@ import javax.xml.transform.stream.StreamSource; | |||
import javax.xml.transform.TransformerConfigurationException; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.taskdefs.XSLTLiaison3; | |||
import org.apache.tools.ant.taskdefs.XSLTLiaison4; | |||
import org.apache.tools.ant.taskdefs.XSLTLogger; | |||
import org.apache.tools.ant.taskdefs.XSLTLoggerAware; | |||
import org.apache.tools.ant.taskdefs.XSLTProcess; | |||
@@ -68,7 +68,7 @@ import org.xml.sax.XMLReader; | |||
* | |||
* @since Ant 1.3 | |||
*/ | |||
public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware { | |||
public class TraXLiaison implements XSLTLiaison4, ErrorListener, XSLTLoggerAware { | |||
/** | |||
* Helper for transforming filenames to URIs. | |||
@@ -118,7 +118,7 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
private Vector outputProperties = new Vector(); | |||
/** stylesheet parameters */ | |||
private Hashtable params = new Hashtable(); | |||
private Hashtable<String, Object> params = new Hashtable<String, Object>(); | |||
/** factory attributes */ | |||
private Vector attributes = new Vector(); | |||
@@ -369,7 +369,7 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
for (final Enumeration enumeration = params.keys(); | |||
enumeration.hasMoreElements();) { | |||
final String name = (String) enumeration.nextElement(); | |||
final String value = (String) params.get(name); | |||
final Object value = params.get(name); | |||
transformer.setParameter(name, value); | |||
} | |||
} | |||
@@ -505,6 +505,16 @@ public class TraXLiaison implements XSLTLiaison3, ErrorListener, XSLTLoggerAware | |||
public void addParam(String name, String value) { | |||
params.put(name, value); | |||
} | |||
/** | |||
* Add a parameter. | |||
* @param name the name of the parameter | |||
* @param value the value of the parameter | |||
* @since Ant 1.9.3 | |||
*/ | |||
public void addParam(String name, Object value) { | |||
params.put(name, value); | |||
} | |||
/** | |||
* Set a logger. | |||
@@ -34,6 +34,46 @@ | |||
resource="${output}/out.xml" | |||
value="set='myvalue'"/> | |||
</target> | |||
<target name="testParameterTypes" depends="setUp" description="parameters of various data types and XPath expressions"> | |||
<property name="antProperty1" value="ANT_PROPERTY_1"/> | |||
<property name="antProperty2" value="ANT_PROPERTY_2"/> | |||
<property name="antProperty3" value="3"/> | |||
<property name="antProperty4" value="substring-before"/> | |||
<xslt in="${legacy.dir}/data.xml" | |||
out="${output}/out.xml"> | |||
<param name="p1" expression="123" type="INT"/> | |||
<param name="p2" expression="64 * 64 div 128 + 10" type="XPATH_NUMBER"/> | |||
<param name="p3" expression="${antProperty4}($antProperty2, '_')" type="XPATH_STRING"/> | |||
<style> | |||
<string><![CDATA[<xsl:stylesheet | |||
version="1.0" | |||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | |||
xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<!-- get the xsl-parameter --> | |||
<xsl:param name="p1"/> | |||
<xsl:param name="p2"/> | |||
<xsl:param name="p3"/> | |||
<!-- use the xsl-parameter --> | |||
<xsl:template match="/"> | |||
p1_result='<xsl:value-of select="$p1 + 321"/>' | |||
p2_result='<xsl:value-of select="$p2"/>' | |||
p3_result='<xsl:value-of select="$p3"/>' | |||
</xsl:template> | |||
</xsl:stylesheet> | |||
]]></string> | |||
</style> | |||
</xslt> | |||
<au:assertResourceContains resource="${output}/out.xml" value="p1_result='444'"/> | |||
<au:assertResourceContains resource="${output}/out.xml" value="p2_result='42'"/> | |||
<au:assertResourceContains resource="${output}/out.xml" value="p3_result='ANT'"/> | |||
</target> | |||
<target name="testInlineStyleSheet" depends="setUp"> | |||
<xslt in="${legacy.dir}/data.xml" | |||