I need some help on looping through an xml file which I manged to get the nodes using xmlproperty but I am struggling on how to loop through them where there are more than one params.
So here is the format:
<Problems>
<Problem>
<Rule>1</Rule>
<ProblemDescription>1</ProblemDescription>
<SourceFile>1</SourceFile>
<Line>1</Line>
<Column>1</Column>
<Severity>Warning</Severity>
</Problem>
<Problem>
<Rule>2</Rule>
<ProblemDescription>2</ProblemDescription>
<SourceFile>2</SourceFile>
<Line>2</Line>
<Column>2</Column>
<Severity>Warning</Severity>
</Problem>
</problems>
I want to loop through this so I can get the following output:
1
1
1
1
1
1
2
2
2
2
2
Solution:
<target>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>
<xmltask source="problem.xml">
<call path="/Problems/Problem">
<param name="rule" path="Rule/text()" />
<param name="probdesc" path="ProblemDescription/text()" />
<actions>
<echo>Rule: #{rule}</echo>
<echo>Problem Description: #{probdesc}</echo>
</actions>
</call>
</target>
You can use an XPATH expression to return everything that matches a given pattern. In your case, the value of certain tags.
Here is an XPATH Ant task.
Haven't used ANT in years, not since I switched to maven.
When I was using ant I would create a custom ant task for this sort of functionality. Then you have the full power of java and far more readable code, at the cost of having to compile the task if you need to make changes.
It really depends on what you are going to do with the output. The other answer with xpath is more appropriate if your doing something really simple.
See:
http://ant.apache.org/manual/develop.html
http://docs.oracle.com/javase/tutorial/jaxp/sax/parsing.html
Related
I need some help with ant. I have a target where I loop over a fileset using foreach from ant-contrib. I call another target(lets call it doStuff) for each of iteration of the loop.
The output of doStuff is something that I would like to store in a file. I would like the files to have unique names and I thought that an integer that gets incremented with every loop would suit me well.
I tried many variations of the code below and had no success. I have probably not understod and yet. It seems to work with immutable properties, making the targets stateless. While I do enjoy that, it doesn't help me with my current problem.
Is there any way to set the myInt in the first target and keep the 'state', increment it with every loop and pass it on to the next target?
<var name="myInt" unset="true"/>
<var name="myInt" value="0"/>
<target name="default">
<foreach target="doStuff" param="theFile">
<fileset dir="" casesensitive="yes">
<depth max="0"/>
</fileset>
</foreach>
</target>
<target name="doStuff" description="Make output directories and run the MXUnit task">
<var name="op1" value="${myInt}"/>
<var name="op2" value="1"/>
<var name="op" value="+"/>
<math result="result" operand1="${op1}" operation="${op}" operand2="${op2}" datatype="int"/>
<var name="myInt" unset="true"/>
<var name="myInt" value="${result}"/>
<!-- Here I save the file with the name ${result}-->
</target>
First of all, a suggestion: consider if it is really necessary to use a self-increment integer -- if you just want a unique, sortable filename, you can use <tstamp> instead.
And this part should be considered as a bad practice to use Ant.
From your description I don't see how your "test" target is called. So I will assume that you just want your "doStuff" to use a self-increment integer each time when it's called.
You can try <script> (example code below is not tested):
<target name="default">
<script language="beanshell" classpathref="your-classpath-ref-id">
String[] theFiles = getProject().getProperty("theFile").split(",");
for (int i = 1; i <= theFiles.length; i++) {
CallTarget antcall = new CallTarget(); // the class of antcall task
antcall.setTarget("doStuff");
Property param1 = antcall.createParam();
param1.setName("number");
param1.setValue(String.valueOf(i));
... // maybe param2 to pass theFiles[i] to doStuff?
antcall.execute();
}
</script>
</target>
If the dependency library of beanshell is not in your Ant's default classpath, you need to include the jar in your classpath with the id "your-classpath-ref-id".
Update
Please read David W's answer to this question:
Ant - How can I run the same depends from multiple targets. This answer gives a good point about what Ant really is -- not a programming language, but a Matrix Dependency Language.
Using a self-increment int with a loop is a feature of a fully featured programming language. If you do want it, you can develop a library like Ant-contrib to provide such a feature. However, I still prefer time stamp over integer. When you processes the filenames as strings, time stamps can be sorted properly without any additional effort, while ints will lead to a result like ["1","10","2","3","4"...].
Hi all please give a look to this code
in my properties file i have
win-x86.pc-shared-location=E:\Ant_Scripts
Now below i am trying to call PrintInstallerName_build from my build.xml,while as PrintInstallerName_build is in test.xml. In build.xml file,${platform.id} has value=win-x86 in the calling target and in called target param1 also has value=win-x86
<target name="PrintInstallerName" >
<echo>PlatForm.Id====>${platform.id}</echo>
<ant antfile="test.xml" target="PrintInstallerName_build">
<property name="param1" value="${platform.id}"/>
</ant>
<target name="PrintInstallerName_build" >
<echo>${param1.pc-shared-location}</echo><!--${param1.pc-shared-location}-->
<echo>${param1}.pc-shared-location}</echo><!--win-x86.pc-shared-location-->
<echo>${win-x86.pc-shared-location}</echo><!--E:\\Ant_Scripts-->
</target>
as you can see only the last statement gives correct output but it is hardcoded,i want to use param1 and the output should be E:\\Ant_Scripts i tried to use $ and # but none works,may be i am doing wrong somewhere can someone help please,i am struck and tomorrow is its DOD.
See Nesting of Braces in the Properties page of the Ant Manual.
In its default configuration Ant will not try to balance braces in
property expansions, it will only consume the text up to the first
closing brace when creating a property name. I.e. when expanding
something like ${a${b}} it will be translated into two parts:
the expansion of property a${b - likely nothing useful.
the literal text } resulting from the second closing brace
This means you can't use easily expand properties whose names are
given by properties, but there are some workarounds for older versions
of Ant. With Ant 1.8.0 and the the props Antlib you can configure Ant
to use the NestedPropertyExpander defined there if you need such a
feature.
You can use <propertycopy> to make it happen.
Consider that you need to have the property value of ${propA${propB}}
Use ant tag of propertycopy as follows:
<propertycopy property="myproperty" from="PropA.${PropB}"/>
<echo >${myproperty}</echo>
This will echo the value of ${propA${propB}}
<target name="PrintInstallerName_process" >
<echo>${param1}</echo><!--win-x86-->
<macrodef name="testing">
<attribute name="v" default="NOT SET"/>
<element name="some-tasks" optional="yes"/>
<sequential>
<echo>Source Dir of ${param1}: ${#{v}}</echo><!-- Dir of Win-x86:E:\Ant_Scripts-->
<some-tasks/>
</sequential>
</macrodef>
<testing v="${param1}.pc-shared-location">
<some-tasks>
</some-tasks>
</testing>
</target>
this is the way it works and for me it works fine anyways #sudocode your tip took me there so thank you very much
I want to write a code in such a way that I will remove platform from first 3 lines and then take only the platform name and I will suffix that with installer-zip.${platform_name}.
platform.win-x86=true
platform.win-x64=true
platform.unix=false
installer-zip.win-x86=E:\abc.jar
installer-zip.win-x64=E:\def.jar
Now if the selected item is win-x86 then printing installer-zip.${platform_name} should give me E:\abc.jar. I tried ${installer-zip.${platform_name}} and many other things but they are not working
You cannot do this with regular ant, but you can do this with ant-contrib.
In particular, there is a contrib task property-regex.
So something like:
<propertyregex property="$newProperty"
input="$oldProperty"
regexp="^platform\.(,*)$"
select="\1"
casesensitive="false" />
EDIT: and then...
<property name=desiredProperty value="installer-zip.${newProperty}" />
That should give you enough to work out the exact solution you're looking for...
Dear, I currently face some problem to retrieve the value of a property setted in a foreach loop. Maybe one of you could help me...
The purpose is to check if one file of a folder has been modified since the corresponding jar has been generated. This way I know if I have to generate the jar again.
What I do is to go through the folder with a foreach loop and if one file match my test, set a property to true.
The problem is that my variable doesn't seems to exist after my loop... Here is a simplified code example that has the same problem:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<foreach target="setVar" param="var" list="a,b"/>
<echo>myreturn in target: ${env.myreturn}</echo>
<property name="env.myreturn" value="c"/>
<echo>myreturn in second: ${env.myreturn}</echo>
</target>
<target name="setVar">
<property name="env.myreturn" value="${var}"/>
<echo>myreturn in setVar: ${env.myreturn}</echo>
</target>
The result of this code is:
target:
setVar:
[echo] myreturn in setVar: a
setVar:
[echo] myreturn in setVar: b
[echo] myreturn in target: ${env.myreturn}
[echo] myreturn in second: c
BUILD SUCCESSFUL
It seems that the variable is correctly set as it could be printed in the "setVar" target but no way to retrieve value from the calling target.
I also know it's not possible to assign a value to a property twice. But the problem doesn't even occurs... When it'll be the case I could add a check on the value of the property before to assign it to be sure it is not already initialized...
Do you have a clue on the way I can solve my problem ???
Many thanks in advance for your help :)
Try <for> task from ant-contrib instead of <foreach>. The <for> task takes advantage of Ant macro facility that came later. It works faster and is more flexible than the older <foreach> task. You are in the same project context when using <for>. That means properties set in the loop will be visible outside of the loop. Of course, normal rules for properties apply... you only get to set it once... unless you use <var> task from ant-contrib to overwrite or unset previously set properties.
Ah the joys of Ant hacking.
Not sure about your foreach problem, but can you not use the uptodate task for your requirement?
Even if I don't need it anymore thanks to sudocode, I found a solution for my question. Maybe it could be useful for someone else...
A collegue talked about the "antcallback" target of ant-contrib: it allows to return a result from a called target to the calling one. With a combination of "for" target and "antcallback" it is possible to do what I wanted to do:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<for param="file">
<path>
<fileset dir="../myDirectory" includes="**/*" />
</path>
<sequential>
<antcallback target="setVar" return="retValue">
<param name="file" value="#{file}"/>
</antcallback>
</sequential>
</for>
<echo>result: ${retValue}</echo>
</target>
<target name="setVar">
<property name="retValue" value="${file}"/>
</target>
"file" contains the name of the file in the directory. It is given to the called target as parameter with value "#{file}" ('#' necessary due to "for" target implementation).
At the end of the main target, ${retValue} contains the first value setted by the "setVar" target. No error is thrown when trying to set it multiple times, so it's not necessary to check if variable has already been instantiated before to set it in "setVar" target.
The <foreach> task uses the same logic as <antcall> under the covers, and any proprrties set inside a target invoked by <antcall> do not have scope beyond the execution of that target.
In other words, the env.myreturn property that you define in the setVar target is lost as soon as execution of that target completes.
This sort of scripting really isn't what Ant is designed for. The Ant-contrib library tries to patch up the holes, but it's still bending it way out of shape.
If you need to write such scripts, and want to use Ant tasks to achieve them, have a look at Gradle instead. It's a rather lovely blend of Groovy (for scripting) and Ant (for the tasks).
The other approaches here (<for>, <var>, <groovy>properties.put(....)</groovy>, <property>, <antcallback>) did not work with ANT 1.9.4, so I used the file system similar to this (pseudocode):
<target name="outer">
<for> <antcall target="inner" /> </for>
<loadproperties srcfile="tmpfile.properties" />
<echo message="${outerprop}" />
</target>
<target name="inner">
<!-- did not work: -->
<!--
<property name="outerprop" value="true" />
<var name="outerprop" value="true" />
<groovy>properties.put('outerprop','true')</groovy>
<antcallback target="setouterprop" />
-->
<echo message="outerprop=true" file="tmpfile.properties" />
</target>
Maybe the other approaches did not work because of my <antcall>, but I need it here. (outerprop is initially unset)
Using Apache Ant 1.7.1
It looks like the Ant Filter task can't resolve the same property several times in one line when recurse is set to true. I can't find any mention of this in the Ant docs. Is this supposed to happen?
Using this ant build file:
<project basedir="." default="assemble" >
<macrodef name="copy-and-filter">
<sequential>
<copy tofile="to.txt" file="from.txt" overwrite="true">
<filterset recurse="true">
<filtersfile file="filters.properties"/>
</filterset>
</copy>
</sequential>
</macrodef>
<target name="assemble">
<copy-and-filter />
</target>
</project>
with these files:
from.txt:
I want my broker to be: #broker.url#
and my client to be: #client.url#
filters.properties:
myval=fish
broker.url=-#myval#-
client.url=#myval#-#myval#
I get the output to be:
i want my broker to be: -fish-
and my client to be: myval
and not what I expected which would be this:
i want my broker to be: -fish-
and my client to be: fish-fish
If I set recurse to false then I get the 'correct' behaviour.
i want my broker to be: -#myval#-
and my client to be: #myval#-#myval#
Why is this?
The recurse flag is intended to look for more tokens after an initial replace occurs, but it won't work if the same token is used again. The problem is that setting recurse=true causes an infinite loop. See the output from Ant:
Infinite loop in tokens. Currently known token tokens: [client.url, myval]
Problem token: #myval# called from #myval#
I don't think its possible to do what you want to be able to do using the filterset task. What are you using the to.txt file for? Is it to provide a system configuration file?