One of the frustrations of using ant was the difficulty of deriving one property value performing some sort of editing operation on an existing property value. The mapper task does a lot of grunt work for file names, but not for property values as such.
A common requirement is to map a Java package name to a corresponding directory structure. I have a property containing the package name, and I want to create another property with the directories. Here's one way to do that.
<property name="my.package" value="my.java.package"/>
<!-- ... Some time later ... -->
<loadresource property="my.package.dirs">
<string value="${my.package}"/>
<filterchain>
<replacestring from="." to="${file.separator}"/>
<striplinebreaks/>
</filterchain>
</loadresource>
For more complex transformations, a replaceregexp filter can be used. The above example would then be:
<property name="my.package" value="my.java.package"/>
<!-- ... Some time later ... -->
<loadresource property="my.package.dirs">
<string value="${my.package}"/>
<filterchain>
<striplinebreaks/>
<replaceregex pattern="\." replace="${file.separator}" flags="g"/>
</filterchain>
</loadresource>
Here's a macrodef to perform string editing, included in an ant build file called editstring.xml with a test invocation. The base file, without the test, can be downloaded
here.
<project name="editstring">
<macrodef name="editstring"
description=
"Edit a string using a regexp pattern and repacement, with optional flags, placing result in a property.">
<attribute name="string" description="String being edited."/>
<attribute name="dest" description="Name of the property receiving the edit result."/>
<attribute name="pattern" description="Regexp pattern to be replaced."/>
<attribute name="replace" description="Regexp replacement pattern."/>
<attribute name="flags" description="Regexp replacement flags." default="-g"/>
<sequential>
<loadresource property="@{dest}">
<string value="@{string}"/>
<filterchain>
<replaceregex pattern="@{pattern}"
replace="@{replace}" flags="@{flags}"/>
<striplinebreaks/>
</filterchain>
</loadresource>
</sequential>
</macrodef>
<target name="junk">
<property name="teststr" value="My test string."/>
<editstring string="${teststr}" dest="result"
pattern="^(\S+\s+)\S+(\s+.*)$" replace="\1result\2"/>
<echo>${teststr} : ${result}</echo>
</target>
</project>
Be aware of the striplinebreaks filter. I found when I used this method that I was getting a spurious newline on the end of my edited string, so I inserted striplinebreaks. However, if you are editing a multiline string, this will break your replacement. You will have to experiment in those circumstances. I suspect that this is an artifact of the line-at-a-time processing of replacement text. I think it appends a line break after replacement.
I first read about this solution in a post by
Mark Melvin. Thanks to Mark.