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="1result2"/> <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.