ant-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Steve Amerige <Steve.Amer...@sas.com>
Subject Ant For Loop: Looping over Ranges with Step and Over PropertySets
Date Thu, 08 Dec 2011 23:46:14 GMT
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

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message