ANT: Load file names and extract data from the file names - ant

Hello I wasn't quite sure how to title this question, but I'll explain why I'm trying to do.
First of all, I have a folder that contains SQL scripts in a specific format, which is updateXtoY.sql, where X and Y are integers. What I need is to know which Y is the highest number. (basically, to know which is the latest script)
So if I have in my folder "scripts/" 3 files:
update3to5.sql
update2to5.sql
update1to6.sql
the result I need is to have assign a property 'latest.version' the value of 6.
From that point I can easily run the script. So the problem I have is 3-fold:
1- How to load the file names into a data structure.
2- How to iterate over the data structure.
3- How to evaluate each file name so that I can extract the "Y" part of the file and get the highest value. (I'm reading on regex right now)
I'm new to ANT and I'm not sure if this is possible and/or feasible.
Thanks for any suggestions.

The first part of the task - getting the filenames into a 'structure' is best done using a FileSet - say for SQL scripts in a directory called scripts:
<fileset dir="scripts" includes="*.sql" id="versions" />
That creates an Ant resource collection that can be referred to using the id versions.
The collection knows about your SQL script files.
Using (as you suggest) a regexp mapper, we can convert the set of files into a collection of strings, holding just the version parts from the filenames:
<mappedresources id="versions">
<fileset dir="scripts" includes="*.sql" />
<regexpmapper from="update.*to(.*).sql" to="\1" />
</mappedresources>
In this example versions now holds a 'list', which would be "5,5,6" for your example files.
It gets trickier now, because you probably need to carry out a numeric sort on what is a list of strings - to avoid 10 sorting as 'less' than 9. Ant ships with an embbeded Javascript interpreter, so you could use that to find the maximum. Another option would be to make use of the numeric sorting capability that ant-contrib has to offer.
Here's a Javascript 'max finder':
<scriptdef name="numeric_max" language="javascript">
<attribute name="property" />
<attribute name="resources_id" />
<![CDATA[
var iter = project.getReference(
attributes.get( "resources_id" )
).iterator( );
var max_n = 0.0;
while ( iter.hasNext() )
{
var n = parseFloat( iter.next() );
if ( n > max_n ) max_n = n;
}
project.setProperty( attributes.get( "property" ), max_n );
]]>
</scriptdef>
That defines a new Ant XML entity - numeric_max - that looks like a task, and can be used to find the numeric maximum of a collection of strings.
It's not perfect - there's no validation of the strings, and I've used floats rather than ints.
Combining that with the mappedresources above:
<mappedresources id="versions">
<fileset dir="scripts" includes="*.sql" />
<regexpmapper from="update.*to(.*).sql" to="\1" />
</mappedresources>
<numeric_max property="latest.version" resources_id="versions" />
<echo message="Latest SQL script version: ${latest.version}." />
When I run that with your three files I get:
[echo] Latest SQL script version: 6.

Related

How to use ant expandproperties with windows pathseparator

I tried to use ants loadproperties with expandproperties:
This works for simple text properties but i get weird results when a property contains a windows path.
<property name="myAntFile" value="${ant.file}" />
<loadproperties srcFile="my.properties">
<filterchain>
<expandproperties />
</filterchain>
</loadproperties>
<echo message="$${external} = ${external}" />
the properties file looks like this:
external=${myAntFile}
the result is:
Buildfile: C:\projects\trunk\build.xml
...
[echo] ${external} = C:projects\trunkbuild.xml
I know that for properties files there are escape rules for backslashes and special whitespace characters. However i dont see how i can translate the buildscripts properties to that special meaning.
Anyone has a idea how to solve that or is this a ant bug (maybe the expandproperties chain should get a additional property for escaping when used in property file contexts?)?
With ant you can use a forward slash / as the path separator when defining paths, even on Windows: C:/projects/trunk/build.xml
If ${ant.file} returns the path using backslashes, convert this path first before you load the properties file.
Unfortunately I haven't yet found the definitive way to convert paths from C:\a\path to C:/a/path and back. Supposedly pathconvert can do the trick...
<pathconvert targetos="unix" property="myAntFile.withForwardSlashes">
<path location="${myAntFile}"/>
</pathconvert>
... but it confuses relative and absolute paths and I couldn't make it work while testing this on my OS X machine.

Iterating through a properties file in Ant

So I need to write an Ant script that iterates through a properties file and uses the keys from that file to pull values from a few other properties files (using the same key).
I haven't been able to find any examples remotely similar to what I'm trying to accomplish. Is this something thats plausible with Ant? I know its rather old. I've never used Maven but I believe our platform would be able to support that if this isn't possible in Ant
Using the <script> command you can execute arbirary java/javascript code and do not supported code.
For your case, perhaps some similar to :
<scriptdef name="iterateprops" language="javascript">
<attribute name="src" />
<![CDATA[
importClass(java.util.Properties);
importClass(java.io.FileInputStream);
var src = attributes.get("src");
var properties = new Properties();
properties.load(new FileInputStream(src));
var names = properties.propertyNames();
while(names.hasMoreElements()) {
println (names.nextElement());
}
]]>
</scriptdef>
An later use it:
<iterateprops src="file.properties" />

Increment integer in ant-contrib

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"...].

ant Task for or foreach loop in xml files

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

Text manipulation in ant

Given an ant fileset, I need to perform some sed-like manipulations on it, condense it to a multi-line string (with effectively one line per file), and output the result to a text file.
What ant task am I looking for?
The Ant script task allows you to implement a task in a scripting language. If you have JDK 1.6 installed, Ant can execute JavaScript without needing any additional dependent libraries. The JavaScript code can read a fileset, transform the file names, and write them to a file.
<fileset id="jars" dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
<target name="init">
<script language="javascript"><![CDATA[
var out = new java.io.PrintWriter(new java.io.FileWriter('jars.txt'));
var iJar = project.getReference('jars').iterator();
while (iJar.hasNext()) {
var jar = new String(iJar.next());
out.println(jar);
}
out.close();
]]></script>
</target>
Try the ReplaceRegExp optional task.
ReplaceRegExp is a directory based task for replacing the occurrence of a given regular expression with a substitution pattern in a selected file or set of files.
There are a few examples near the bottom of the page to get you started.
Looks like you need a conbination of tasks:
This strips the '\r' and '\n' characters of a file and load it to a propertie:
<loadfile srcfile="${src.file}" property="${src.file.contents}">
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.StripLineBreaks"/>
</filterchain>
</loadfile>
After loading the files concatenate them to another one:
<concat destfile="final.txt">
...
</concat>
Inside concat use a propertyset to reference the files content:
<propertyset id="properties-starting-with-bar">
<propertyref prefix="src.file"/>
</propertyset>
rodrigoap's answer is enough to build a pure ant solution, but it's not clean enough for me and would be some very complicated ant code, so I used a different method: I subclassed ant's echo task to make an echofileset task, which takes a fileset and a mapper. Subclassing echo buys me the ability to output to a file. A regexmapper performs the transformation on filenames that I need. I hardcoded it to print out each file on a separate line, but if I needed more flexibility I could add an optional separator attribute. I also thought about providing the ability to output to a property, too, but it turned out I didn't need it since I echo'ed straight to a file.

Resources