Hi all,
In the recent past, I've asked about implementing a "for" loop in Ant. I've wanted something that could iterate over number ranges
or propertysets. And, I wanted something that didn't depend on Ant-Contrib 1.0b3. And, I wanted to be able to iterate forwards or
backwards with a settable step value.
I was able to work out everything except for handling the element. Enter: Scot Floess. We got together via e-mail and had a great
time, and in just a few days hammered out a solution that works! Many thanks for helping finish the for loop code, Scot! The
solution is not perfect in that it uses global properties (the index, key, and value attributes used as ${index}, ${index.key}, and
${index.value}) instead of params (used as @{param}) in the element body. But this imperfection is mitigated by the fact that the
code below works and that by choosing names carefully results in code that is acceptable for production use. The code below follows
naming conventions... see if you can guess what they are.
Very importantly, the code below shows how to execute an element. This opens
up many possibilities for coding in Ant!
The most interesting parts of the code below that relate to elements:
* <__for-internal ...><sequential><body/></sequential></__for-internal>
o Note that this organization allows us to depend on the sequential task
existing and that the body element is the very first
thing in it.
* <element name="sequential"
classname="org.apache.tools.ant.taskdefs.Sequential"/>
o We bind the "sequential" element to the Sequential taskdef.
* Task body = (Task) elements.get("sequential").get(0)
o And, here we get the 0th object: the body element.
* body.execute()
o Now, we can execute the body element.
I will put this code up on a website so that if I fix any bugs, you can grab
the latest version.
Please feel free to improve on the code below and please do share with me directly or in this list with your comments and/or
improvements. Again, thanks to Scot for his energetic, fun, and useful contributions!
Enjoy!
Steve Amerige
SAS Institute, Deployment Software Development
*Ant For Loop Implementation, Test Code, and Results*
<macrodef name="__for">
<attribute name="index" description="index name (stores int values
from start..end by step)"/>
<attribute name="start" default="1" description="starting value"/>
<attribute name="end" default="" description="ending value (not applicable for
refid)" />
<attribute name="step" default="1" description="increment"/>
<attribute name="refid" default="" description="reference to a set, typically a
propertyset"/>
<element name="body" implicit="true" description="for body" />
<sequential>
<if>
<equals arg1="@{refid}" arg2=""/>
<then>
<__for-internal index="@{index}" start="@{start}" end="@{end}" step="@{step}">
<sequential>
<body/>
</sequential>
</__for-internal>
</then>
<else>
<var name="_!string" value=", ${toString:@{refid}}"/>
<propertyset id="_!set">
<propertyset refid="@{refid}"/>
<mapper type="glob" from="*" to="~~*" />
</propertyset>
<propertyregex property="_!list" input=", ${toString:_!set}" regexp="(^~~|, ~~)" replace="|" global="true" override="true"
defaultValue="" />
<var name="@{index}" value="@{start}"/>
<var name="_!key" value="@{index}.key"/>
<var name="_!value" value="@{index}.value"/>
<for list="${_!list}" delimiter="|" param="foritem">
<sequential>
<propertyregex property="${_!key}" input="@{foritem}" regexp="^([^=]*)=.*" select="\1"
override="true" defaultValue="" />
<propertyregex property="${_!value}" input="@{foritem}" regexp="^[^=]*=(.*)" select="\1"
override="true" defaultValue="" />
<body/>
<math result="@{index}" operand1="${@{index}}" operand2="@{step}" operation="+"
datatype="int"/>
</sequential>
</for>
</else>
</if>
</sequential>
</macrodef>
<scriptdef name="__for-internal" language="groovy">
<attribute name="index"/>
<attribute name="start"/>
<attribute name="end"/>
<attribute name="step"/>
<element name="sequential"
classname="org.apache.tools.ant.taskdefs.Sequential"/>
<![CDATA[
import org.apache.tools.ant.Task
index = attributes.get("index")
start = attributes.get("start").toInteger()
end = attributes.get("end").toInteger()
step = attributes.get("step").toInteger()
Task body = (Task) elements.get("sequential").get(0)
for (int i = start; (step > 0) ? (i <= end) : (i >= end); i += step) {
project.setProperty(index, i.toString())
body.execute()
}
]]>
</scriptdef>
*Test Code*
<__for index="i" start="1" end="10">
<echo message="i = ${i}"/>
</__for>
<property name="a.b.c.d" value="1"/>
<property name="a.b.c.e" value="2"/>
<property name="a.c.x.x" value="3,4,5"/>
<property name="a.c.x.y" value="3"/>
<property name="a.c.x.z" value="3"/>
<property name="p.q.r" value="4"/>
<property name="p.q.s" value="4"/>
<property name="p.q.t" value="4 />
<propertyset id="r">
<propertyref regex="^a\.(?!b\.).*"/> <!-- begins with "a." not followed by "b."
-->
</propertyset>
<__for index="_i" refid="r">
<echo message="_i = ${_i} key='${_i.key}' value='${_i.value}'"/>
</__for>
*Test Results*
test-forloop:
[echo] i = 1
[echo] i = 2
[echo] i = 3
[echo] i = 4
[echo] i = 5
[echo] i = 6
[echo] i = 7
[echo] i = 8
[echo] i = 9
[echo] i = 10
[echo] _i = 1 key='a.c.x.x' value='3,4,5'
[echo] _i = 2 key='a.c.x.y' value='3'
[echo] _i = 3 key='a.c.x.z' value='3'
BUILD SUCCESSFUL
Total time: 1 second