Get number of lines in property using resourcecount - java

I am trying to get a ANT-Buildscript to count the lines that are stored inside a ANT-property. From the examples I got the way to count the lines in a file, like this:
<resourcecount count="0" when="eq">
<tokens>
<concat>
<filterchain>
<tokenfilter>
<linetokenizer/>
</tokenfilter>
</filterchain>
<fileset file="${file}" />
</concat>
</tokens>
</resourcecount>
Now I want to refer to a ANT-property instead of the file. Is there a way to do this? I know about the solution to write the contents of the property into a file using <echo file="${temp.file}">${the.property.with.many.lines}</echo> and using the code above after. But I wonder if there is a solution that works without a temporary file.

A propertyresource element may used in place of the fileset as follows:
<property name="lines"
value="line01${line.separator}line02${line.separator}line03"/>
<target name="count-lines">
<resourcecount property="line.count" count="0" when="eq">
<tokens>
<concat>
<filterchain>
<tokenfilter>
<stringtokenizer delims="${line.separator}" />
</tokenfilter>
</filterchain>
<propertyresource name="lines" />
</concat>
</tokens>
</resourcecount>
<echo message="${line.count}" />
</target>
Output
count-lines:
[echo] 3
BUILD SUCCESSFUL
Total time: 0 seconds

Related

Placeholder for value/version/date replacement in xml using Ant build

I have more than 30 odx-d files (odx-d is just xml file with different extension).
All files have common tags:
<DOC-REVISION>
<REVISION-LABEL>01.02.03-04</REVISION-LABEL>
<STATE>RELEASE</STATE>
<DATE>2018-11-14T16:26:00+01:00</DATE>
</DOC-REVISION>
At every release I need to change these values in all files.
Note: Manipulation using Java is not possible as while build just making zip of all these files not using Java to manipulate these files.
Please suggest a way to have one file (any file type you suggest) where I can have these values and place holders for the tags in all these files.
Thanks.!
This is doable with the following steps:
replace the common tag values with placeholders e.g. #revision#,
#state#, #date#
copy each file to a temporary location
perform the replacements in the copied files using a <replace file="${dest.file}"> task with nested <replacefilter .../> elements
zip the transformed files in the temporary location
For example, using a template file "template.xml" like this:
<DOC-REVISION>
<REVISION-LABEL>#revision#</REVISION-LABEL>
<STATE>#state#</STATE>
<DATE>#date#</DATE>
</DOC-REVISION>
you can set the real values with this ant target (skipping the zip part):
<target name="test">
<property name="my.revision" value="01.02.03-04"/>
<property name="my.state" value="RELEASE"/>
<tstamp>
<format property="my.date" pattern="yyyy-MM-dd hh:mm z"/>
</tstamp>
<property name="template.file" value="./template.xml"/>
<property name="dest.file" value="./doc.odx"/>
<delete file="${dest.file}" quiet="true"/>
<copy toFile="${dest.file}" file="${template.file}"/>
<replace file="${dest.file}">
<replacefilter token="#revision#" value="${my.revision}"/>
<replacefilter token="#state#" value="${my.state}"/>
<replacefilter token="#date#" value="${my.date}"/>
</replace>
</target>
Solution for multiple files.
Replace values with placeholders #revision#, #state#, #date# and place into template folder.
Perform the copy operation with filterset from template to dest directory.
Example:
Template dir: 'fromDir', destination: 'toDir'
1) Template files:
<DOC-REVISION>
<REVISION-LABEL>#revision#</REVISION-LABEL>
<STATE>#state#</STATE>
<DATE>#date#</DATE>
</DOC-REVISION>
2) Declare properties and perform test target operation.
<!-- Properties -->
<property name="version" value="01.02.03-04" />
<property name="state" value="RELEASE" />
<tstamp>
<format property="now" pattern="yyyy-MM-dd'T'HH:mm:ss.SSSXXX"/>
</tstamp>
<!-- Target -->
<target name="test">
<copy todir="${toDir}">
<fileset dir="${fromDir}" />
<filterset>
<filter token="revision" value="${version}" />
<filter token="state" value="${state}" />
<filter token="date" value="${now}" />
</filterset>
</copy>
</target>
Thanks!

Ant: Apply task: Adding an extra argument per input file in parallel="true" mode

I have an ant build file in which:
<target name="foo">
<apply executable="bar" parallel="true">
<fileset dir="." includes="*.xxx" />
<srcfile prefix="--session " />
</apply>
</target>
This will call bar with a single argument of --session a.xxx for each file of the type .xxx in the current directory. How can I get ant to invoke bar with two arguments for each file of the type .xxx?
In the ideal situation bar would receive two arguments, --session and a.xxx for each file of the type .xxx.
It's a little late but I hope anyone who faces this problem in the future can consider my solution and hopefully solve your problem.
For my example, I am trying to execute yuicompressor with multiple options operating on multiple input files:
<apply executable="java" parallel="true">
<fileset dir="./js-ori">
<include name="**/*.js" />
</fileset>
<arg line="-jar" />
<arg path="yuicompressor.jar" />
<arg line="--nomunge" />
<arg line="--preserve-semi" />
<srcfile />
<arg line="-o" />
<arg line="&apos;.js$:.min.js&apos;" />
</apply>
Since apply command with 'parallel="true"' will only apply the options on the first input file, the result is not what is intended. Instead of having ANT pass in the input files, pass in the input files manually using < arg line >.
<!-- Get the fileset as a string -->
<fileset id="myFileSet" dir=".">
<include name="js/**/*.js" />
<exclude name="js/lib/**" />
</fileset>
<property name="fileset" refid="myFileSet" />
<!-- Replace all ';' of the string of files from fileset to your intended options -->
<replaceStringWithRegExp string="${fileset}"
searchPattern=";"
replacementPattern=" --nomunge --preserve-semi "
property="result"/>
<!-- We will disallow <apply> to take in input files by itself since we are already appending the input files manually. -->
<!-- Therefore we add 'addsourcefile="false"' -->
<apply executable="java" parallel="true" addsourcefile="false">
<fileset dir="./js">
<include name="**/*.js" />
<exclude name="lib/**" />
</fileset>
<arg line="-jar" />
<arg path="yuicompressor-2.4.7.jar" />
<arg line="-o" />
<arg line="&apos;.js$:.min.js&apos;" />
<arg line="${result}" /> <!-- pass in the string of options and file names -->
</apply>
This is my solution for executing yuicompressor once with multiple input files and options. I am unsure about other execution of .jar files but the solution should have a similar concept. If you find any part of the code that could be improved, please sound it out to me! Thank you!
Note: If you don't want to install ant-contrib for , you can use the replaceStringWithRegExp function created by Giorgio Ferrara in the link below.
Reference
Thanks Giorgio Ferrara for the replaceStringWithRegExp function!
You can use an extra <arg/> tag to include --session as a separate argument:
<target name="foo">
<apply executable="bar" parallel="false">
<fileset dir="." includes="*.xxx" />
<arg value="--session" />
<srcfile />
</apply>
</target>

Ant Propertyregex part of file path

I am looping list of files inside base directory using for of Ant-Contrib library. I want to get the part of file path after base directory but without filename.
For example my base Directory is : C:\projects\Dev\Main\Sample Game\js
I have lot of js scripts and inside this folder and subfolders name like
C:\projects\Dev\Main\Sample Game\js\simple\welcome\ss.js
C:\projects\Dev\Main\Sample Game\js\hard\welcome\cc.js
C:\projects\Dev\Main\Sample Game\js\easy\welcome\ee.js
I want to get only \simple\welcome\. I am using the below code.
http://pastebin.com/TpqXBb27
<for param="filename">
<path id="project.fileset">
<fileset dir="${basedir}/js" includes="/*">
<include name="**/*.js" />
</fileset>
</path>
<sequential>
<basename property="file.#{filename}" file="#{filename}"/>
<propertyregex property="currentdirectory"
input="#{filename}"
regexp="${basedir}/js//([^//]*)//${file.#{filename}}"
select="\1"
casesensitive="false"
override="true" />
<echo message="FullPath:#{filename}" />
<echo message="Directory:${currentdirectory}" />
</sequential>
</for>
I don't know what regex I should provide there... I tried with regex ${basedir}/js//([^//]*)//${file.#{filename}}, but getting the below error
java.util.regex.PatternSyntaxException: Illegal/unsupported escape sequence near index 3
C:\projects\Dev\Main\Sample Game\js\simple\welcome\ss.js
Please give some suggestion for this regex
This sounds like more of a job for pathconvert rather than propertyregex
<for param="filename">
<path id="project.fileset">
<fileset dir="${basedir}/js" includes="/*">
<include name="**/*.js" />
</fileset>
</path>
<sequential>
<basename property="file.#{filename}" file="#{filename}"/>
<!-- dirname strips off the trailing file name, leaving the full dir -->
<dirname property="dir.#{filename}" file="#{filename}"/>
<pathconvert property="currentdirectory.#{filename}">
<file file="${dir.#{filename}}" />
<!-- pathconvert strips off the leading ${basedir}/js -->
<map from="${basedir}/js" to="" />
</pathconvert>
<echo message="FullPath:#{filename}" />
<echo message="Directory:${currentdirectory.#{filename}}" />
</sequential>
</for>
The problem is that you are on a PC, and when you use ${basedir}, it's giving you the ${basedir} with backward Windows slashes for directory separators. The <fileset/> is doing the same.
I ran into this very same situation, and I had to first do a regexreplace on my path, then do my substitution. Here's the code directly from my project:
<var name="dest.name" unset="true"/>
<var name="file.lastmodified" unset="true"/>
<var name="file.type" unset="true"/>
<file.mdate file="#{file}"
property="file.lastmodified"/>
<propertyregex property="file"
input="#{file}"
regexp="\\"
replace="/"
override="true"
defaultvalue="#{file}"/>
<propertyregex property="dest.name"
input="${file}"
regexp="^${fileset}/"
replace=""
override="true"/>
Note that I now have to use <var/> to unset the properties, so I can use them over and over in my <for> loop:
The first regexreplace will change
C:\projects\Dev\Main\Sample Game\js\simple\welcome\ss.js
to
C:/projects/Dev/Main/Sample Game/js/simple/welcome/ss.js
From there, you can use the basedir task to remove the script. Then, you just need to for the /js/ directory.
<regexreplace property="currentdirectory"
file="#{file}"
regex=".*/js/(.*)"
select="\1"
override="true"/>

How do I build a list of file names?

I need to write an Ant target that appends together (comma-delimited) a list of '.jar' file names from a folder into a variable, which is later used as input to an outside utility.
I am running into barriers of scope and immutability. I have access to ant-contrib, but unfortunately the version I am stuck with does not have access to the 'for' task. Here's what I have so far:
<target name="getPrependJars">
<var name="prependJars" value="" />
<foreach param="file" target="appendJarPath">
<path>
<fileset dir="${project.name}/hotfixes">
<include name="*.jar"/>
</fileset>
</path>
</foreach>
<echo message="result ${prependJars}" />
</target>
<target name="appendJarPath">
<if>
<equals arg1="${prependJars}" arg2="" />
<then>
<var name="prependJars" value="-prependJars ${file}" />
</then>
<else>
<var name="prependJars" value="${prependJars},${file}" />
</else>
</if>
</target>
It seems that 'appendJarPath' only modifies 'prependJars' within its own scope. As a test, I tried using 'antcallback' which works for a single target call, but does not help me very much with my list of files.
I realize that I am working somewhat against the grain, and lexical scope is desirable in the vast majority of instances, but i really would like to get this working one way or another. Does anybody have any creative ideas to solve this problem?
You might be able to use the pathconvert task, which allows you to specify the separator character as comma.
<target name="getPrependJars">
<fileset id="appendJars" dir="${project.name}/hotfixes">
<include name="*.jar" />
</fileset>
<pathconvert property="prependJars" refid="appendJars" pathsep="," />
<echo message="prependJars: ${prependJars}" />
</target>
I'd simply write a custom task in Java that (1) takes the folder name, (2) assembles the result string and (3) stores it to the ${prependJars} property.
In ant you just define the task (taskdef) and use like all other tasks afterwards.
I did it once when I was faced with a simliar problem and found that it was very, very easy.
Here's the tutorial.
If a system path format is useful to you, you can use the following:
<target name="getPrependJars">
<path id="prepend.jars.path">
<fileset dir="${project.name}/hotfixes">
<include name="*.jar"/>
</fileset>
</path>
<property name="prependJars" value="${toString:prepend.jars.path}" />
<echo message="result ${prependJars}" />
</target>

Is it possible to replace text in properties in Ant's build.xml?

I have a property, app.version, which is set to 1.2.0 (and, of course, always changing) and need to create zip file with name "something-ver-1_2_0". Is this possible?
You can use the pathconvert task to replace "." with "_" and assign to a new property:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<property name="app.version" value="1.2.0"/>
<pathconvert property="app.version.underscore" dirsep="" pathsep="" description="Replace '.' with '_' and assign value to new property">
<path path="${app.version}" description="Original app version with dot notation" />
<!--Pathconvert will try to add the root directory to the "path", so replace with empty string -->
<map from="${basedir}" to="" />
<filtermapper>
<replacestring from="." to="_"/>
</filtermapper>
</pathconvert>
<echo>${app.version} converted to ${app.version.underscore}</echo>
</project>
Another approach is to filter the version number from a file to a property using a regular expression, as suggested in this example:
<loadfile srcfile="${main.path}/Main.java" property="version">
<filterchain>
<linecontainsregexp>
<regexp pattern='^.*String VERSION = ".*";.*$'/>
</linecontainsregexp>
<tokenfilter>
<replaceregex pattern='^.*String VERSION = "(.*)";.*$' replace='\1'/>
</tokenfilter>
<striplinebreaks/>
</filterchain>
</loadfile>
Since the property app.version is always changing i assume you don't want to hard code it into the properties files, rather pass it when you do the build. Further to this answer, you can try the following on the command line;
ant -f build.xml -Dapp.version=1.2.0
changing app.version to the one required then.
Edit:
Understood better your question from the feedback. Unfortunately ant does not have string manipulation tasks, you need to write you own task for this. Here is a close example.
It's possible using the zip task
<zip zipfile="something-ver-${app.version}.zip">
<fileset basedir="${bin.dir}" prefix="bin">
<include name="**/*" />
</fileset>
<fileset basedir="${doc.dir}" prefix="doc">
<include name="**/*" />
</fileset></zip>
For more information about the zip task: http://ant.apache.org/manual/Tasks/zip.html
Properties can't be changed but antContrib vars (http://ant-contrib.sourceforge.net/tasks/tasks/variable_task.html ) can.
Here is a macro to do a find/replace all on a var:
<macrodef name="replaceVarText">
<attribute name="varName" />
<attribute name="from" />
<attribute name="to" />
<sequential>
<local name="replacedText"/>
<local name="textToReplace"/>
<local name="fromProp"/>
<local name="toProp"/>
<property name="textToReplace" value = "${#{varName}}"/>
<property name="fromProp" value = "#{from}"/>
<property name="toProp" value = "#{to}"/>
<script language="javascript">
project.setProperty("replacedText",project.getProperty("textToReplace").split(project.getProperty("fromProp")).join(project.getProperty("toProp")));
</script>
<ac:var name="#{varName}" value = "${replacedText}"/>
</sequential>
</macrodef>
Then call the macro like:
<ac:var name="newFileName" value="${app.version}"/>
<current:replaceVarText varName="newFileName" from="." to="_" />
<echo>Filename will be ${newFileName}

Categories