Copy the value from a property in an Ant propertyfile to another property - replace

I need to copy the value of one property in a propertyfile to a second property, and add that to the propertyfile.
For example, if I have a property file Test.properties containing
2018=jan;feb;mar
2019=jan;feb;mar
2020=jan;feb;mar
********************************
name=john,math,sudha
my input property is "2020" and output is "2021", after running Ant Test.properties should contain
2018=jan;feb;mar
2019=jan;feb;mar
2020=jan;feb;mar
2021=jan;feb;mar
********************************
name=john,math,sudha
with out changing the order How could I do that?

You might be able to use something based on the following:
The idea is to read the propertyfile, then use two <propertyset> instances with the <echoproperties> task to update the file.
<property name="prop.file" value="Test.properties" />
<property name="input" value="2020" />
<property name="output" value="2021" />
<property name="pf" value="prefix" />
<property name="input.prop" value="${pf}.${input}" />
<loadproperties srcfile="${prop.file}" prefix="${pf}" />
<echoproperties destfile="${prop.file}">
<propertyset>
<propertyref prefix="${pf}" />
<mapper type="glob" from="${pf}.*" to="*" />
</propertyset>
<propertyset>
<propertyref name="${input.prop}" />
<mapper type="glob" from="${input.prop}*" to="${output}*" />
</propertyset>
</echoproperties>
There's more code there than you might expect: the "prefix" is being used to ensure that the properties loaded from the file don't clash with any in your Ant buildfile as properties are imutable.
The order of the propertysets in the echoproperties task is important, especially if there is already a value for property "2021" in the file that you are updating. Where properties appear in both sets, the value in the last propertyset seen "wins" and is echoed to the output file.

<property name="prop.file" value="test.properties" />
<property name="input" value="2020" />
<property name="output" value="2021" />
<replaceregexp file="${prop.file}"
match="${input}(=)(.*)"
replace="${input}=\2${line.separator}${output}=\2"
flags="gi"
byline="true" />

Related

Changing the value of XML file based on key using Ant

I have a xml snippet like below
<bean id="aqConnectionFactoryInitialiser" class="foo.aqjms.spring.OracleAqConnectionFactoryInitialiser">
<property name="URL" value="jdbc:oracle:thin:#192.168.80.182:1234:foo"/>
<property name="username" value="foo"/>
<property name="password" value="xAU2oMLjNXU1GrKbcsjvMQ=="/>
</bean>
Using Ant, I only need to change value="foo" to value="bar" based on name="username" I tried the below snippet but it replaced all foo occurrences
<replace file="E:/vipul/drchanges_testing/testsnippet.xml">
<replacefilter token="foo" value="bar" />
</replace>
How can I achieve this using Ant?
first you need to select vlaue="foo" using this :
(?<=name="username")\s+(value=".*?")
then replace it by value="bar"
demo for regex

Ending target early in Phing

I am trying to modify a Phing script and cannot see what I thought would be an obvious feature.
The Phing script has a generic 'confirm' target which checks for input at various stages of execution. I want to automate the script so that it can run without input. I would expect to be able to to this by inserting some kind of <break> or <end> type task within the target so that it returns early. The manual does't appear to list such functionality.
I know I can achieve this, by creating an intermediate target to check the cmd line argument first and then call the confirm target, but is there a more elegant way?
This is the target that needs automating and is called from multiple places. The trigger to skip would be a property set via cmd line -D.
<!-- confirm a user action -->
<target name="confirm">
<input propertyname="confirm" validargs="yes,no">
${confirm.message} ('yes' to continue)
</input>
<if>
<not>
<equals arg1="${confirm}" arg2="yes" />
</not>
<then>
<fail message="You didn't say 'yes'" />
</then>
</if>
</target>
For the same purpose I'm using this tech:
<if>
<not>
<isset property="confirm" />
</not>
<then>
<input propertyname="confirm" validargs="yes,no">
${confirm.message} ('yes' to continue)
</input>
<if>
<not>
<equals arg1="${confirm}" arg2="yes" />
</not>
<then>
<fail message="You didn't say 'yes'" />
</then>
</if>
</then>
</if>
then you can execute
phing build.xml -Dconfirm=yes
without promt.

MSBuild RegEx Assembly Version

Trying to create an MSBuild task that outputs my code to a folder. All is working except the Regular Expression. My code:
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(OutDir)$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="TheVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<Pattern>(\d+)\.(\d+)\.(\d+)</Pattern>
<In>%(TheVersion.Version)</In>
<OutVersion>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</OutVersion>
</PropertyGroup>
<ItemGroup>
<OutputFiles Include="$(OutDir)*" Exclude="*.tmp" />
<SolnFiles Include="$(SolutionDir)INDIVIDUAL.txt;$(SolutionDir)LICENSE.txt;$(SolutionDir)README.md" />
</ItemGroup>
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
</Target>
When I run this, I get this error:
The item "D:\Dache\INDIVIDUAL.txt" in item list "SolnFiles" does not define a value for metadata "OutVersion". In order to use this metadata, either qualify it by specifying %(SolnFiles.OutVersion), or ensure that all items in this list define a value for this metadata.
When I try %(SolnFiles.OutVersion) it comes up blank. I'm doing something dumb here, what is it?
Took me a few to figure it out. PropertyGroup variables are referenced as $(Var) while ItemGroup output variables are #() and GetAssemblyIdentity is %() - so I changed:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
to this:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\" SkipUnchangedFiles="true" />
and it worked.

Adding a property to a manytomany join in Transfer ORM (Coldfusion)

I am using transfer and I have a ManyToMany relationship between articles and videos. What I need to be able to do is to stick a timestamp against each video when it is added to an article. i.e. I have two tables:
article (ID, title, body)
video (ID, url)
I then have a linked table:
article_videos(articleID, videoID)
I need to add in an extra column timeStamp to article_videos:
article_videos(articleID, videoID, timeStamp)
The problem I have is that when I try and create an extra property in the link table it does not work.
My Transfer ORM configuration:
<package name="article">
<object name="Article" table="article">
<id name="ID" type="numeric"/>
<property name="Title" type="string" column="title"/>
<property name="Body" type="string" column="body"/>
<manytomany name="Videos" table="article_videos">
<link to="article.Atricle" column="articleID"/>
<link to="assets.Video" column="videoID"/>
<collection type="array">
<order property="OrderIndex" order="asc"/>
</collection>
<property name="TimeStamp" type="timestamp" column="timeStamp"/>
</manytomany>
</object>
</package>
<package name="assets">
<object name="Video" table="video">
<id name="ID" type="numeric"/>
<property name="url" type="string" column="url"/>
</object>
</package>
The problem is that the new property inside the ManyToMany is not allowed, it throws an error saying that the Transfer configuration is malformed.
Where and how should I add the timestamp baring in mind that the timestamp needs to be for that video in that article as the video may be used in multiple articles?
Thanks in advance.
What you will likely have to do is create a new object for your article_videos join.
Because Transfer ORM handles many-to-many joins transparently so you don't directly interact with the join table if you wanted to add and access additional properties on the join you will need to create a new object. There are a couple of ways to achieve this.
If you still wanted to handle the many-to-many relationship transparently and the timestamp will be automatically populated by the database you can keep the relationship as is and add a new object representing article_videos and a new relationship joining that object to both article and video objects.
So you would add a new object representing the article_videos, I might also add a surrogate key to the database or you might want to use a composite ID:
<object name="ArticleVideo" table="article_videos">
<id name="ID" type="numeric"/>
<property name="TimeStamp" type="timestamp" column="timeStamp"/>
</object>
Then you would update your Article object to reference this new object:
<object name="Article" table="article">
<id name="ID" type="numeric"/>
<property name="Title" type="string" column="title"/>
<property name="Body" type="string" column="body"/>
<manytomany name="Videos" table="article_videos">
<link to="article.Article" column="articleID"/>
<link to="assets.Video" column="videoID"/>
<collection type="array">
<order property="OrderIndex" order="asc"/>
</collection>
</manytomany>
<onetomany name="ArticleVideo">
<link to="article.ArticleVideo" column="articleID"/>
<collection type="array">
<order property="TimeStamp" order="asc"/>
</collection>
</onetomany>
</object>
And you would also update your Video object:
<object name="Video" table="video">
<id name="ID" type="numeric"/>
<property name="url" type="string" column="url"/>
<onetomany name="ArticleVideo">
<link to="article.ArticleVideo" column="videoID"/>
<collection type="array">
<order property="TimeStamp" order="asc"/>
</collection>
</onetomany>
</object>
This way you can use the Article to Video object relationship as normal but access the additional properties if you need to:
// Creating the join using the many-to-many relationship
article = transfer.get("article.Article", 1);
article.addVideo(video);
You can also then access the join, how you get the information to correlate the above relationship to the join might require a bit of work, so you will likely have to decide up front whether the join you are creating you will want more info:
// Creating the join using the ArticleVideo object
articleVideo = transfer.new("article.ArticleVideo");
articleVideo.addParentArticle(article);
articleVideo.addParentVideo(video);
transfer.save(articleVideo);
// Using a composite ID to access the ArticleVideo
articleVideo = transfer.get("article.ArticleVideo", {
"ArticleID" = 1,
"VideoID" = 1
});
WriteOutput("The timestamp is: #articleVideo.getTimeStamp()#");
Otherwise you can adjust all of the relationships, but this will require you using an intermediate object between your videos and articles. If it is just a timestamp, then it may be unnecessary.

I want to use NAnt's foreach to iterate files in a folder, how to force alphabetic iteration?

I have an NAnt task "ship" to package my current .sql scripts into a build, then name the build with an incrementing int {######} and copy it to a build folder.
I have another NAnt task which executes those build scripts.
They must execute in order, but in my last attempt, they were not. Can I "force" NAnt to work alphabetically?
FAIL:
<fileset basedir="source\tsql\builds\" id="buildfiles">
<include name="*.sql.template.sql" />
<exclude name="*.sql" />
<exclude name="*asSentTo*" />
</fileset>
<foreach item="File" property"filename">
<in refid="buildfiles">
<echo message="${filename}" />
</in>
</foreach>
PASS:
<foreach item="File" property="filename" in="source\tsql\builds">
<do>
<if test="${string::ends-with(filename,'.sql.template.sql')}">
<echo message="${filename}" />
</if>
</do>
</foreach>
To satisfy my curiosity I tried to reproduce the problem with this script:
<?xml version="1.0"?>
<project name="foreach.test" default="foreach.alpha">
<target name="foreach.alpha">
<foreach item="File" in="C:\foo" property="filename">
<do>
<echo message="${filename}" />
</do>
</foreach>
</target>
</project>
The filenames are printed out in alphabetical order. So conventional use of foreach already seems to be the solution to the problem.
Here is how you do it with a fileset
<fileset id="mySet">
<include name="*.sql" />
</fileset>
<copy>
<fileset refid="mySet" />
</copy>
<foreach item="File" property="filename">
<in>
<items refid="mySet" />
</in>
<do>
<echo message="Copied files: ${filename} to directory: ${Folder}." />
</do>
</foreach>