Details
-
Improvement
-
Status: Closed
-
Minor
-
Resolution: Fixed
-
1.0-JSR-2
-
None
Description
The new replaceAll method of String, which takes a closure as a parameter, is
very handy when some transformation is needed on the found strings other than
simple replacement. However, the parameter supplied to the closure is the
found string which isn't helpful when operation on individual groups is
needed. It would add a great deal of flexibility if individual groups could
be manipulated as well. The code below is one way to do that. Using
MatchAccessor as the closure parameter rather than the Matcher itself ensures
that the closure doesn't mess with the matcher, e.g. by calling reset which
would result in infinite regress. By overriding toString() and plus() it is
also convenient to use in string expressions in the closure. (Besides,
Matchers getAt() calls reset(), which precludes it from being used while
iterating over the matches).
I would also vote for having the Matchers getAt method only return groups and
not reset the matcher. It could then be used in the in Matcher.each without
going into infinite loop. (The getAt method as it is now, resets the matcher
and returns the index'th group of the first match if any groups are defined,
otherwise it returns the index'th match of the whole expression. I suspect
this latter use case, which is probably the rational for resetting the
matcher, is somewhat rare.)
// DefaultGroovyMethods.java
public String replaceAll(String self, String regex, Closure closure) {
Matcher matcher = Pattern.compile(regex).matcher(self);
Match match= new Match(matcher);
StringBuffer buffer = new StringBuffer();
while (matcher.find())
matcher.appendTail(buffer);
return buffer.toString();
}
public static String getAt(Matcher matcher, int idx)
{ idx = normaliseIndex(idx, matcher.groupCount()); return matcher.group(idx); } // Provides access to the current match of a Matcher.
public static class Match {
Matcher matcher;
public Match(Matcher matcher)
{ this.matcher = matcher; }public String toString()
{ return matcher.group(0); }public Object getAt(int index)
{ index = normaliseIndex(index, matcher.groupCount()); return matcher.group(index); }public Object plus(Object object)
{ return matcher.group(0) + object; }}