Browse Source

Allow params of XSLT to be optionally typed. PR 21525. Submitted by František Kučera

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1548237 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 11 years ago
parent
commit
f0565366fe
9 changed files with 372 additions and 16 deletions
  1. +1
    -0
      CONTRIBUTORS
  2. +3
    -0
      WHATSNEW
  3. +4
    -0
      contributors.xml
  4. +86
    -2
      manual/Tasks/style.html
  5. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java
  6. +41
    -0
      src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.java
  7. +182
    -10
      src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
  8. +14
    -4
      src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
  9. +40
    -0
      src/tests/antunit/taskdefs/xslt-test.xml

+ 1
- 0
CONTRIBUTORS View File

@@ -121,6 +121,7 @@ Ernst de Haan
Frank Harnack
Frank Somers
Frank Zeyda
Frantisek Kucera
Frederic Bothamy
Frederic Lavigne
Gary S. Weaver


+ 3
- 0
WHATSNEW View File

@@ -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
===================================



+ 4
- 0
contributors.xml View File

@@ -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>


+ 86
- 2
manual/Tasks/style.html View File

@@ -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>
&lt;/xslt&gt;
</pre>
<h4>Using XSL parameters</h4>
<p>Simple String parameter:</p>
<pre>
&lt;xslt basedir=&quot;doc&quot; destdir=&quot;build/doc&quot;
extension=&quot;.html&quot; style=&quot;style/apache.xsl&quot;&gt;
@@ -469,6 +504,55 @@ with <a href="../Types/propertyset.html">syspropertyset</a>s.</p>
element &lt;xsl:param name=&quot;date&quot;/&gt;, the variable
<code>$date</code> will subsequently have the value 07-01-2000.
</p>
<p>Various data types and XPath expressions:</p>
<pre>&lt;property name="antProperty1" value="ANT_PROPERTY_1"/&gt;
&lt;property name="antProperty2" value="ANT_PROPERTY_2"/&gt;
&lt;property name="antProperty3" value="3"/&gt;
&lt;property name="antProperty4" value="substring-before"/&gt;

&lt;!--
${this} is substituted by Ant itself
and $this is evaluated by XPath as a variable
--&gt;

&lt;xslt in="in.xml" out="out.xml" style="template.xsl"&gt;
&lt;!-- Simple String parameter: --&gt;
&lt;param name="p0" expression="some nice string" type="STRING"/&gt;
&lt;!-- A value substituted by Ant --&gt;
&lt;param name="p1" expression="some string with ${antProperty1} constructed by Ant" type="STRING"/&gt;
&lt;!-- XPath resulting in: and this is done in XPath: ANT_PROPERTY_2 --&gt;
&lt;param name="p2" expression="concat('and this is done in XPath: ', $antProperty2)" type="XPATH_STRING"/&gt;
&lt;!-- Some XPath math, result: 42 --&gt;
&lt;param name="p3" expression="64 * 64 div 128 + 10" type="XPATH_NUMBER"/&gt;
&lt;!-- Some numeric parameter: --&gt;
&lt;param name="p4" expression="123.45" type="DOUBLE"/&gt;
&lt;!-- XPath expression, result: true boolean --&gt;
&lt;param name="p5" expression="$antProperty1 = 'ANT_PROPERTY_1'" type="XPATH_BOOLEAN"/&gt;
&lt;!-- First one is an XPath variable, second one is a text substituted by Ant, result: true boolean --&gt;
&lt;param name="p6" expression="$antProperty2 = '${antProperty2}'" type="XPATH_BOOLEAN"/&gt;
&lt;!-- Some XPath math with a variable, result: 64 --&gt;
&lt;param name="p7" expression="$antProperty3 * 4 * 5 + 4" type="XPATH_NUMBER"/&gt;
&lt;!--
XPath expression with substituted function name and a variable:
substring-before($antProperty2, '_')
result: ANT
--&gt;
&lt;param name="p8" expression="${antProperty4}($antProperty2, '_')" type="XPATH_STRING"/&gt;
&lt;!-- Without type attribute: --&gt;
&lt;param name="p9" expression="default type is String"/&gt;
&lt;/xslt&gt;</pre>

<h4>Using output properties</h4>
<pre>


+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java View File

@@ -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;


+ 41
- 0
src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.java View File

@@ -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;
}

+ 182
- 10
src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java View File

@@ -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


+ 14
- 4
src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java View File

@@ -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.


+ 40
- 0
src/tests/antunit/taskdefs/xslt-test.xml View File

@@ -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"


Loading…
Cancel
Save