Groovy - Replace ${} within a string - groovy

I receive a string like this: "The code is ${code}. Your currency is ${currency}".
The ${...} characters are already part of the string, not variables.
I have a map with key-value and I would like to replace all the occurrences of the ${key} with the value:
def myMap = [
'code' : '123456',
'currency' : 'CHF'
]
myMap.each{
fieldValue.replaceAll("${$.key}", it.value)
}
The expected result is the following one: "The code is 123456. Your currency is CHF".
I'm stuck because I don't know how to deal with the $ {} character escapes. I can escape the $ character but not {}. Any idea in order to achieve the expected result?

You need to quote the outer $ and use it.key: "\\${$it.key}"
Also you can use each here as it is for side-effects and replaceAll
does not modify the string (strings in java are immutable). So you need
something like this:
def result = myMap.inject(fieldValue){ acc, kv ->
acc.replaceAll("\\${$kv.key}", kv.value)
}
Or by using a regexp:
fieldValue.replaceAll(/\$\{(.+?)\}/, { _, k -> myMap[k] })

It works also with closure and with delegate strategy. You can evaluate your string in the context of the map. See this example:
def myMap = [
'code' : '123456',
'currency' : 'CHF'
]
closure = { "The code is ${code}. Your currency is ${currency}" }
closure.delegate = myMap
println closure()

Related

How to validate a Map snippet in groovy

I have an dynamic html file that groovy is generated from. Part of this html template format is {routeId}{groovyMap} like so
USER_FORM[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]
or something like
USER_FORM[name: 'Dean', user: user]
I made the first example more complex. Currently, I split on ':' and validate all the keys supplied. What I would like to do is take the groovy snippet and grab all the keys and validate
1. all keys are strings
2. validate the keys against some meta data I already have
I do not care about the values at all. Currently, I split on ':' but obviously that won't work for all cases. I am worried about other complex cases I may not be thinking about.
This is for a templating engine and I prefer to failfast if possible making it easier on the user when something is wrong.
I concur with others that you want to avoid parsing directly.
If you use GroovyShell, you can dope the input string with no-op methodMissing and propertyMissing handlers. In this way, even the complex example will work.
See code below, including test-cases (extracting map string from the "USER_FORMstr" format is left to the reader).
class KeyGenerator {
// these could be "final static". omitted for brevity
def shell = new GroovyShell()
def methodMissingHandler = "def methodMissing(String name, args) {}"
def propertyMissingHandler = "def propertyMissing(String name) {}"
def generateKeys(mapStr) {
def evalInput = "${methodMissingHandler} ; " +
"${propertyMissingHandler} ; " +
"${mapStr}"
def map = shell.evaluate(evalInput)
return map.keySet()
}
}
// ------- main
def keyGenerator = new KeyGenerator()
def expected = new HashSet()
expected << "name"
expected << "user"
def mapStr = "[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]"
assert expected == keyGenerator.generateKeys(mapStr)
def mapStr2 = "[name: 'Dean', user: user]"
assert expected == keyGenerator.generateKeys(mapStr2)
If I got you right, you can use something like:
String val = "USER_FORM[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]"
def res = []
val.eachMatch( /[\[,] ?(\w+):/ ){ res << it[ 1 ] }
assert '[name, user, item, day]' == res.toString()
all keys are strings
When using the literal syntax for creating a Map, i.e.
Map m = [foo: 'bar']
as opposed to
Map m = new HashMap()
m.put('foo', 'bar')
the keys are always strings, even if you have a variable in scope with the same name as the key. For example, in the following snippet, the key will be the string 'foo', not the integer 6
def foo = 6
Map m = [foo: 'bar']
The only way you can create a Map using the literal syntax with a key that is not a string is if you have a variable in scope with the same name as the key and you wrap the key name in parentheses. For example, in the following snippet, the key will be the integer 6, not the string 'foo'
def foo = 6
Map m = [(foo): 'bar']
Currently, I split on ':' but obviously that won't work for all cases. I am worried about other complex cases I may not be thinking about.
Parsing a map literal using regex/string splitting seems like a bad idea as you'll likely end up badly re-implementing the Groovy lexer. Something like the following seems a better option
def mapString = '[foo: "bar"]'
Map map = Eval.me(mapString)
// now you can process the map via the Map interface, e.g.
map.keySet().toList() == ['foo']

how can i append to each line in scala and why is it saying unit?

im doing something stupid to try to send html email and not really understanding what im doing but I want to send a multiline string to a function and get the same string back with something appended to each line - what am i doing wrong?
def htmlizetext(intext: String) {
for(line <- intext.linesWithSeparators) {
<br>line<br/>
}
}
def htmlizetext(intext: String): String = {
for(line <- intext.linesWithSeparators) {
line + "<br/>"
}
}
Neither of the above work
You need yield, = (without = the method will still return Unit) and some form of concatenation:
def htmlizetext(intext: String) = {
for (line <- intext.linesWithSeparators) yield {
line + "<br/>
}
}.mkString
or the shorter equivalent:
def htmlizetext(intext: String) =
intext.linesWithSeparators.map(_ + "<br/>").mkString
Have a look at yield, that is probably what you are looking for.
def htmlizetext(intext: String) = {
for(line <- intext.linesWithSeparators) yield {
<br>line<br/>
}
}
You might additionally want to join all the elements from the returning list for returning a single String instead of a list of such strings
You are currently not returning a thing from your method as you do not return anything from your for statement which is the last method of your statement. Therefore, the Scala compiler infers that you are returning Unit.
Because for does't have a return value, unless you use yield...
A better solution would be to "map" your collection as follows:
intext.linesWithSeparators.map(line=> s"$line<br/>").mkString("")
this will turn your string in the desired format and then join all of them using mkString. If you need it, you can specify a separator instead of empty string

Groovy unusual convention

I have seen strange code for many times:
...
private currencyFormat = NumberFormat.currencyInstance
def convert = currencyFormat.&parse
...
By strange I mean this -> .&parse. Why logical AND operator is needed and who else it can be used?
That's a method pointer
convert is now effectively a Closure which delegates to the parse method
Example
It's defined here without an example, and mrhaki did a post about it here.
And as an example showing it handles overloading, consider a class with 2 static methods:
class Test {
static void printSomething( String thing ) {
println "A:$thing"
}
static void printSomething( String thing, String thing2 ) {
println "B:$thing$thing2"
}
}
We can get a reference to the printSomething methods:
def ref = Test.&printSomething
And then we can pass it to an each call on a list of single items:
// prints A:a and A:b
[ 'a', 'b' ].each ref
Or we can pass it two items and it will pick the correct overloaded method to call:
// prints B:ab and B:cd
[ [ 'a', 'b' ], [ 'c', 'd' ] ].each ref
It's not a bit manipulation operator. The ampersand was probably chosen because that's what C's address operator uses. The idea is that you can pass around references to a method on a specific instance.
Say you have some logic like:
def stuff = null
if (condition) {
stuff = foo.doThis(a, b, c)
} else {
stuff = bar.doOther(a, b, c)
}
You can rewrite that with a method pointer like this:
def myMethod = condition ? foo.&doThis : bar.&doOther
def stuff = myMethod(a, b, c)
It doesn't mean and. It's special syntax to reuse a method as a closure. Now you can use convert in places where you can pass a closure, like grep, find or similar methods.

how to get rid of "null" when concating string in groovy?

I have a class
class A{
String name
String address
}
def a = new A()
a.address = "some address"
println "${a.name} ${a.address}" => "null some address"
Here a.name is null, so the string printed will contains "null", however I hope the result is "some address" which ignore the null value.
I know I can use println "${a.name ?: ''} ${a.address ?: ''}" when printing, is there any simpler solution?
You could redefine the toString method for Groovy's null object to return an empty string instead of null.
def a = [a:null, b:'foobar']
println "${a.a} ${a.b}"
org.codehaus.groovy.runtime.NullObject.metaClass.toString = {return ''}
println "${a.a} ${a.b}"
This will print:
null foobar
foobar
If you only want to redefine toString temporarily, add the following after your last print... to change it back:
org.codehaus.groovy.runtime.NullObject.metaClass.toString = {return 'null'}
You can also change null's toString behavior using a Groovy Category [1] [2]. For example:
#Category(org.codehaus.groovy.runtime.NullObject) class MyNullObjectCategory {def toString() {''}}
use (MyNullObjectCategory) {
println "${a.a} ${a.b}"
}
You could implement a toString method in your class like so:
class A{
String name
String address
String toString() {
"${name ?: ''} ${address ?: ''}".trim()
}
}
then do
def a = new A( address:'some address' )
println a
To get some address printed out, but this still used the Elvis operator as you had in your question...
Not sure there's much simpler you can do...
Not sure if simpler, but:
[a.name, a.address].findAll().join(' ')
You may of course combine it with Tim's toString suggestion.
Notice that if any of the values might be "falsy" (e.g. 0), it will filter it out. You can fix that doing:
[a.name, a.address].findAll {it != null}.join(' ')
I think, a rather simple way of achieving it, i.e. removing null, is to concatenate the string and the use replace method.
myString=""
myString=myString + "Bla Bla"
myString.replace("null", '')

What does this code do in Groovy?

def host = /\/\/([a-zA-Z0-9-]+(\.[a-zA-Z0-9-])*?)(:|\/)/
assertHost 'http://a.b.c.d:8080/bla', host, 'a.b.c.d'
def assertHost (candidate, regex, expected){
candidate.eachMatch(regex){assert it[1] == expected}
}
I know the above code is asserting my inputs! But in line 4, inside the closure, the magic variable (it) is being represented in an array! I'm bit confused on it. How does it work?
How does this work in Groovy (illustrate with simple code)?
From http://groovy.codehaus.org/groovy-jdk/java/lang/String.html:
replaceAll
public String replaceAll(String regex, Closure closure)
Replaces all occurrences of a captured group by the result of a closure on that text.
For examples,
assert "hellO wOrld" == "hello world".replaceAll("(o)") { it[0].toUpperCase() }
assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() })
Here,
it[0] is the global string of the matched group
it[1] is the first string in the matched group
it[2] is the second string in the matched group

Resources