tags:

views:

504

answers:

7

Ok I give up, I've been trying to write a regexp in ant to replace the version number from something that I have in a properties file. I have the following:

<?xml version="1.0" encoding="UTF-8"?>
    <feature
          id="some.feature.id"
          label="Some test feature"
          version="1.0.0"
          provider-name="Provider">

   <plugin
         id="test.plugin"
         download-size="0"
         install-size="0"
         version="0.0.0"
         unpack="false"/>
..... many plugins later....
</feature>

What I want to achieve is substitute the version number of the feature tag only, without changing the version of the xml or the version of the miriad of plugins in the file.

The problem I have is that I either match too much or too little. Definitively matching "version" is not enough, because everything would be changed

Is there any easy way to match then only the version inside the tag, taking into consideration that the '0.0.0' could be any number?

A: 

A Perl substitution regex would look something like this...

s/<feature(.*?)version=".*?"/<feature$1version="1.2.3.4"/s
Paul Dixon
A: 

How about:

<feature[^<>]+version\s*=\s*"(\d+\.\d+\.\d+)"[^<>]*>

In the parentheses is the group that matches the version number. Move the parentheses around to get the exact match you require.

Tomas Sedovic
That matches the version number, but can't be used for a Regex.Replace.
Will
+3  A: 

The following regex may do what you want...

(?<=<feature[^>]{*,1000}version=")[^"]*(?=")

Which, in human speak, roughly means

I want to match text that immediately follows the string "<feature", followed by up to a thousand characters not the greater-than bracket, and "version=""; the text to match is immediately followed by a double quote, and contains no double quotes.

**Thanks to Alan M for the heads up about java lookbehinds ಠ_ಠ

If you use this regex with a Replace operation, it will switch out whatever is inside the version="" attribute with whatever you set as the replacement value. Its up to you to provide the correctly formatted version number.

Now, since you're doing this with XML, the obvious question is, "Why aren't you using XPath?"

Will
In Java regexes, every lookbehind has to have an obvious maximum length, so a lookbehind with `[^>]*` in it won't compile. You can often fudge that requirement by plugging in an arbitrary number, as in `[^>]{0,100}`.
Alan Moore
Thanks for the heads up on that one.
Will
Would have had to add another library to the ANT build... seems overkill just for a quick replace operation... Or so I though until I saw how much fun regexp is :D. But you are right, it is better suited to something like XPath
Mario Ortegón
+1  A: 

Assuming you're using the replaceregexp task:

<replaceregexp file="whatever"
               match="(<feature\b[^<>]+?version=\")[^\"]+"
               replace="\1${feature.version}" />

I'm also assuming there's only the one <feature> element.

Alan Moore
Greetings! This is exactly what I was trying to do, but hail to the regexp guru.The only gotcha is that several characters must be escaped: match="(<feature\b[^<>]+?version=")[^"]+"And it worked like a charm! (Edit? it is a nice answer!)
Mario Ortegón
As if the regexp syntax wasn't cryptic enough, throw in some escaping :D
Mario Ortegón
Oh, right: XML escapes! At least I remembered not to double up all the backslashes, as I'm used to doing in Java string literals. :-/
Alan Moore
A: 

Vim ti the rescue:

:s/\(label=".+"\_.\s*version="\).+"/\1NEWVERSIONNUMBER"/
Zsolt Botykai
A: 

Just for fun: a one-liner, using xpath(!), with ruby

(I know: it is not a regexp, but it is meant to illustrate the suggestion of Will, who is saying

Now, since you're doing this with XML, the obvious question is, "Why aren't you using XPath?

)

type 'irb', then:
require "rexml/document";include REXML;file = File.new("a.xml"); doc = Document.new(file);puts doc; doc.elements.each("/feature") {|e| e.attributes["version"]="1.2.3" }; puts doc

It replaces all 'version' attributes of all 'feature' elements with "1.2.3"

irb(main):001:0* require "rexml/document";include REXML;file = File.new("a.xml"); doc = Document.new(file);puts doc; doc.elements.each("/feature") {|e| e.attributes["version"]="1.2.3" }; puts doc

<?xml version='1.0' encoding='UTF-8'?>
<feature id='some.feature.id' version='1.0.0' provider-name='Provider' label='Some test feature'>
   <plugin unpack='false' id='test.plugin' download-size='0' version='0.0.0' install-size='0'/>
</feature>


<?xml version='1.0' encoding='UTF-8'?>
<feature id='some.feature.id' version='1.2.3' provider-name='Provider' label='Some test feature'>
   <plugin unpack='false' id='test.plugin' download-size='0' version='0.0.0' install-size='0'/>
</feature>
VonC
+1  A: 

It might be a little heavyweight, but since you're dealing with XML, I would recommend using an XSLT like this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="@*|*">
     <xsl:copy>
      <xsl:apply-templates select="node()|@*|*"/>
     </xsl:copy>
    </xsl:template>


    <xsl:template match="/feature/@version">
     <xsl:attribute name="version">
      <xsl:text>1.0.1</xsl:text>
     </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>
Adam Crume