Ant replace token from properties file - ant

i would like to replace tokens in source files with Ant:
some test ${foo} other text ...
Tokens are contained in a properties file eg.:
foo=1
Actually this is easy if tokens in source files were like '##foo##' or 'foo' but i'm not able to replace whole token : ${foo}
I've succeed years ago but this time i've failed ...
Thanks

This is somewhat similar to these.
Properties are loaded from the file properties.txt.
The weakness here is that all occurrences of ${ in your input text are converted to { before the token substitution - which may well break things if that string appears elsewhere in the text. If that is a problem, it still aught to be possible to adapt this solution.
<copy file="input.txt" tofile="output.txt">
<filterchain>
<replaceregex pattern="\$\{" replace="{" />
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="propertiesfile" value="properties.txt"/>
<param type="tokenchar" name="begintoken" value="{"/>
<param type="tokenchar" name="endtoken" value="}"/>
</filterreader>
</filterchain>
</copy>

Related

Is there a way to make #suffix on task basename in Apache Ant case-insensitive?

in my build file, I have declared:
<basename property="filename" file="${args.input}" suffix="XML"/>
where ${args.input} is passed in through an Oxygen transformation scenario, ex -Dargs.input="${cfd}\PMC-min.XML"
${filename} returns PMC-min, which is the desired output. I want the file name without any extension.
However, after discovering an error I realized that
<basename property="filename" file="${args.input}" suffix="xml"/>
was returning PMC-min.XML. So #suffix is case-sensitive.
I could change ${args.input} to ${cfd}\PMC-min (which would require other changes to the build file), or just make sure the extension case of ${args.input} matches #suffix in the scenario. But I was wondering if there was a case-insensitive way to retrieve the filename without the extension in ant? (It doesn't seem to matter if the case of the actual file's extension is different, only the parameters have to match).
One way that might work for you is to use a <mappedresources> "collection of one", something like this:
<mappedresources id="converted">
<string value="${args.input}" />
<chainedmapper>
<flattenmapper />
<globmapper casesensitive="false" from="*.xml" to="*" />
</chainedmapper>
</mappedresources>
<echo message="file_name=${toString:converted}" />
The echo is just to illustrate how to reference the resource.
In the above:
the string resource specifies the input value
the chained mapper combines two mapping steps to alter the string:
a flattenmapper removes the directory part
a globmapper removes the suffix, ignoring case.

ant replace tokens how to ignore new lines?

I am copying a file while doing search and replace tokens in ant using replaceregex tag
tokens exist in a separate text file specified in the propertiesfile
token1=SDF234\n1sdfad
token2=FooBarBaz\nBar
the config.ini file has replacement values as : ${token1}
Some tokens have \n in them and the replaceregex adds a new line
I tried to add : byline="true" without success
I also added flags="m" to the replaceregex tag, it didn't work
sample token:
QWpVsL2Ti\njWhT\n
the code replaces it like:
QWpVsL2Ti
jWhT
the desired replacement should be in the same line ignoring the \n
ant task:
<copy file="../../config.ini" tofile="./Package/config.ini">
<filterchain>
<replaceregex pattern="\$\{" replace="{" byline="true"/>
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="propertiesfile" value="properties_${build_target}.ini"/>
<param type="tokenchar" name="begintoken" value="{"/>
<param type="tokenchar" name="endtoken" value="}"/>
</filterreader>
</filterchain>
</copy>

Create a fileset from a semicolon-separated list in a property

I have an ant property ${src.dirs} that contains a list of dirs separated by a semi colon.
Now i need to specify fileset (for replaceregexp) and that fileset has to contain all java files from all dirs listed in ${src.dirs}.
How can i do it (I don't use any ant-contrib funcky stuff, I use plain vanilla ant).
The src.dirs have this form: /usr/work/dir1/src;/usr/work/java/dir2/src;/usr/libabc/src
There's is an example on how to use propertyregex, but when I try to use it I get this error:
build.xml:98: Problem: failed to create task or type propertyregex
Edit:
Here's what was my final solution:
<loadresource property="source.dir.javafiles">
<propertyresource name="source.dir"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="\s*([;,]\s*)*$" replace="/**/*.java"/>
<replaceregex pattern="\s*([;,]\s*)+" replace="/**/*.java," flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<fileset dir="" includes="${source.dir.javafiles}"/>
These regexes ensure that trailing commas or semicolons don't produce wrong fileselectors.
You might be able to do this without using ant-contrib. Here's a possibility:
<property
name="dirlist"
value="/usr/work/dir1/src;/usr/work/java/dir2/src;/usr/libabc/src" />
<property name="file.wildcard" value="*.java" />
<loadresource property="dirs.include">
<propertyresource name="dirlist"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="^/" replace="" />
<replaceregex pattern=";/" replace="/**/${file.wildcard}," flags="g"/>
<replaceregex pattern="$" replace="/**/${file.wildcard}" />
</tokenfilter>
</filterchain>
</loadresource>
<fileset id="files" dir="/" includes="${dirs.include}" />
The work is split into two: first string processing to convert the semicolon-separated list into patterns suitable for use in a fileset includes attribute; second make a fileset from the pattern.
The loadresource task here is simply being used as a wrapper around a sequence of simple regular expression replacements. The three replacements deal with the leading root directory \, expanding the intra-string semicolons into Ant patterns and commas (which are used in includes attributes to separate entries), and adding a pattern at the end of the string.
In your case you might consider tuning this to not use the root directory in the dir attribute of the fileset.
propertyregex is from ant-contrib, which is why the example is not working for you.
Here is one way to achieve what you want.
<pathconvert property="src.dirs.includes" pathsep="/**/*.java,">
<path path="${src.dirs}" />
</pathconvert>
<replaceregexp match="\s+" replace=" " flags="g" byline="true">
<files id="files" includes="${src.dirs.includes}/**/*.java" />
</replaceregexp>
However spaces in any of the filenames (including their path) will stuff you up.
Do you simply have to go through these directories and do your compile, or must these directories be compiled together because of dependencies?
If there are no dependencies, you could try the <for/> task in Ant-Contrib. This lets you loop through a list like the one you have:
<for list="${src.dirs}"
param="my.src.dir"
delimiter=";">
<sequential>
<javac destdir="${javac.destdir}"
srcdir="#{my.src.dir}"
classpathref="main.classpath"/>
</sequential>
</for>
Of course, you might have to munge things for your correct destdir. You may find the <var/> task convenient when you use the <for/> task. The <var/> task allows you to reset variable names. When you repeat the <sequential/> set of tasks, you may find you want to reset certain properties.
By the way, if you have Ant 1.8 or higher, you can use the <local/> task instead of <var/>.

How do I remove non-tokens during copy in Ant?

I have files that I need cleaned up during the build process. There is a fixed string that I need removed everywhere it appears. The files are being copied so during that copy I tried including a filterset where the token is the text to be removed and the value is the empty string. This didn't work because I set begintoken and endtoken to the empty string and Ant didn't like that.
This is not a one time operation so it needs to be part of the build process. The files contain SQL INSERT statements and are used to populate tables at runtime. Each line references the schema plus the table name and I need just the table name, e.g.
insert into Schema1.Table1 ...
should be
insert into Table1 ...
Thank you!
Use a nested filterchain with tokenfilter, something like =
<copy todir="...">
<fileset dir="..." />
<filterchain>
<tokenfilter>
<replacestring from="Schema1." to="" />
</tokenfilter>
</filterchain>
</copy>
if you need regexp for replacement use =
...
<tokenfilter>
<replaceregex pattern="..." replace="..." flags="".../>
</tokenfilter>
...
instead.

Can I send Ant 'replace' task output to a new file?

The Ant replace task does an in-place replacement without creating a new file.
The below snippet replaces tokens in any of the '*.xml' files with the corresponding values from the 'my.properties' file.
<replace dir="${projects.prj.dir}/config"
replacefilterfile="${projects.prj.dir}/my.properties"
includes="*.xml" summary="true" />
I want those files that had their tokens replaced to be created named after a pattern (e.g.) '*.xml.filtered', and keep the original files.
Is this possible in Ant with some smart combination of tasks?
There are a couple of ways to get close to what you want without copying to a temporary directory and copying back.
Filtersets
If the source files can be changed so that the parts to be replaced can be delimited with begin and end tokens, as in #date# (# is the default token, but it can be changed) then you can use the copy task with a globmapper and a filterset:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
<filterset filtersfile="replace.properties" />
</copy>
If replace.properties contains FOO = bar, then any occurrence of #FOO# in a source xml file file be replaced with bar in the target.
Note that the source and target directories are the same, the globmapper means the target files and named with the suffix .filtered. It's possible (and more usual) to copy files into a different target directory)
Filterchains
If the source file can't be changed to add begin and end tokens, a possible alternative would be to use a filterchain with one or more replacestring filters instead of the filterset:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
<filterchain>
<tokenfilter>
<replacestring from="foo" to="bar" />
<!-- extra replacestring elements here as required -->
</tokenfilter>
</filterchain>
</copy>
This will replace any occurrence of foo with bar, anywhere in the file, which is more like the behaviour of the replace task. Unfortunately this way means you need to include all your replacements in the build file itself, you can't have them in a separate properties file.
In both cases the copy task will only copy source files that are newer than the target files, so unnecessary work won't be done.
Copy then replace
A third possibility (that has just occured to me whilst writing up the other two) would be to perform the copy first to the renamed files, then run the replace task specifying the renamed files:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
</copy>
<replace dir="config" replacefilterfile="replace.properties" summary="true"
includes="*.xml.filtered" />
This might be the closest solution to the original requirement. The downside is that the replace task will be run each time on the renamed files. This could be a problem for some replacement patterns (admittedly they would be odd ones like foo=foofoo, but they would be okay with the first two methods) and you will be doing unnecessary work when the dependencies don't change.
The replace task doesn't observe dependencies, instead it carries out the replacement by writing a temporary file for each input file. If the temporary file is the same as the input file, it is discarded. A temporary file that differs from the input file is renamed to replace that input. This means all the files are processed, even if none of them need be - hence it can be inefficient.
The original solution to this question was to carry out a copy-replace-copy. The second copy isn't needed though, as a mapper can be used in the first. In the copy, dependencies can be used to restrict processing to just the files that have changed - by means of a depend selector in an explicit fileset:
<copy todir="${projects.prj.dir}">
<fileset dir="${projects.prj.dir}">
<include name="*.xml" />
<depend targetdir="${projects.prj.dir}">
<mapper type="glob" from="*.xml" to="*.xml.filtered" />
</depend>
</fileset>
<mapper type="glob" from="*.xml" to="*.xml.filtered" />
</copy>
That will restrict the copy fileset to just those files that have changed. An alternative syntax for the mappers is:
<globmapper from="*.xml" to="*.xml.filtered" />
The simplest replace would then be:
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties"
includes="*.xml.filtered" />
That will still process all the files though, even if none of them need undergo replacements. The replace task has an implicit fileset and can operate on an explicit fileset, but unlike similar tasks the implicit fileset is not optional, hence to take advantage of selectors in an explicit fileset you must make the implicit one 'do nothing' - hence the .dummy file here:
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties">
includes=".dummy" />
<fileset dir="${projects.prj.dir}" includes="*.xml.filtered">
<not>
<different targetdir="${projects.prj.dir}">
<globmapper from="*.xml.filtered" to="*.xml" />
</different>
</not>
</fileset>
</replace>
That will prevent the replace task from needlessly processing files that have previously undergone substitution. It doesn't, however, prevent processing of files that haven't changed and don't need substitution.
Beyond that, I'm not sure there is a way to 'code golf' this problem to reduce the number of steps to one.
There isn't a multiple string replacement filter that can be used in a copy task to achieve the same affect as replace, which is a shame because that feels like it would be the right solution.
One other approach would be to generate the xml for a series of replace string filters and then have Ant execute that. But that will be more complex than the existing solution, and prone to problems with replacement strings that, if pasted into an xml fragment will result in something that can't be parsed.
Yet another approach would be to write a custom task or script task to do the work. If there are many files and the copy-replace solution is judged to be too slow, then this might be the way to go. But again, that approach is less simple than the existing solution.
If the requirement is to minimise the work done in the processing, rather than to come up with the shortest Ant solution, then this approach might do.
Make a fileset containing a list of inputs that have changed.
From that fileset create a comma-separated list of corresponding filtered files.
Carry out the copy on the fileset.
Carry out the replace on the comma-separated list.
A wrinkle here is that the implicit fileset in the replace task will fall back to processing everything if no files have changed. To overcome this we insert a dummy file name.
<fileset id="changed" dir="${projects.prj.dir}" includes="*.xml">
<depend targetdir="${projects.prj.dir}">
<globmapper from="*.xml" to="*.xml.filtered" />
</depend>
</fileset>
<pathconvert property="replace.includes" refid="changed">
<map from=".xml" to=".xml.filtered" />
</pathconvert>
<copy todir="${projects.prj.dir}" preservelastmodified="true">
<fileset refid="changed" />
<globmapper from="*.xml" to="*.xml.filtered" />
</copy>
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties"
includes=".dummy,${replace.includes}" summary="true" />

Resources