I would think there would be many applications for the subject in general. I can illustrate with an example. Consider that I want a way to pull out a standard (maven) version from a released jar file. Regular expressions can be used something like:
def m = (~/.*-someUniquePortionHere-(.*)\.jar$/).matcher(jarFileName)
m.matches()
String version = "${m.group(1)}"
So the above is over-simplified, but, it works for that UniquePortion - and it helps keep the question simple. If I want to wrap this in a method that takes the unique portion in as a parameter, can I still use the slashy strings? I would like to include "$uniquePortion" in the regular expression. That is, what should the following be?
def exampleMethod(String uniquePortion, String jarFileName) {
// what would this look like here?
def m =
m.matches()
"${m.group(1)"
}
Why not? Slashy strings ARE GStrings as well. see link
def unique = "groovy-all"
def m = (~/.*$unique(.*)\.jar$/).matcher("groovy-all:2.2.jar")
m.matches()
String version = "${m.group(1)}"
assert version == ":2.2"
You can still concatenate the slashy strings together:
def test = "foo-bar-1.4.jar"
def bar = "bar"
def m = test =~ /.*-/ + bar + /-(.*).jar$/
assert m.matches() // true
assert m.group(1) == "1.4" // true
Related
I am taking a JSON input and I want it to convert it to uppercase. Can someone please help me with my code
int synchronizeSingleUnit(ApiResultDTO apiResultDTO, def inputJSON, int totalUpdates) {
def sql = synchronizationApiSqlHelperService.getUnitsSql()
String unit = getEmptyIfValueNull(inputJSON.unit)
def session = sessionFactory_apiDb.openSession() as SessionImpl
def connection = session.connection()
def sqlConnection = new Sql(connection)
try {
sqlConnection.execute(sql, [unit:unit])
} catch (Exception ex) {
// Preload result with statement to be executed
apiResultDTO.setJsonFailingPart(inputJSON)
apiResultDTO.setFailedSql(sql, [unit:unit])
throw new ExceptionWrapper(apiResultDTO, ex)
} finally {
session.close()
connection.close()
}
You can use java String.toUpperCase() as in:
String unit = getEmptyIfValueNull(inputJSON.unit)
String uCaseUnit = unit.toUpperCase()
< -- edit -- >
As a comment and addition, I don't know the specifics of method getEmptyIfValueNull but judging from the name you just want to return an empty string when the expression inputJSON.unit returns null.
Groovy has two special operators which make expressions like these easier to write.
the safe navigation operator ?. and
the elvis operator ?: (see how that looks like an Elvis smiley?)
using these two you could rewrite your code somewhat more concisely as:
String unit = inputJSON.unit?.toUpperCase() ?: ''
explanation:
inputJSON.unit?.toUpperCase() - evaluate inputJSON.unit and if that expression returns null, just return null from the entire expression (never executing the toUpperCase method). If inputJSON.unit returns a non-null value, things work as they would just by using inputJSON.unit.toUpperCase().
... ?: '' - take an expression and if it is not empty string or null,return it, otherwise return the empty string.
Where the first operator .? is specifically for handling null values and the second operator ?: uses groovy truth which includes, but is more inclusive than just null values.
There are a few ways of writing the above, for example:
String unit = (inputJSON.unit ?: '').toUpperCase()
but to my mind the first version "flows" better. To each his own.
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']
Let's say I have
def A = "abc"
def X = "xyz"
how do I create a Map where, instead of
def map = [A:1, X:2]
I get instead the equivalent of writing
def map = [abc:1, xyz:2]
but can use a variables A and X for the key?
P.S.: Same question for the value part of the map.
Use this:
def map = [(A):1, (X):2]
For the value-part it's even easier, since there is no automagic "convert text to string" happening:
def map = [keyA:A, keyX:X]
Further to Joachim's answer, if you want to add entries to an existing map and the keys are variables, use:
def map = [:]
def A = 'abc'
map[A] = 2
If you use:
map.A = 2
It is assumed that you want to use the literal string 'A' as the key (even though there is a variable named A in scope.
Update
As #tim_yates pointed out in a comment, a key variable will also be resolved if you use:
map."$A" = 2
though personally I prefer to use the [A] syntax because refactoring tools might miss the "$A" reference if the variable is renamed
as a tcl developer starting with groovy, I am a little bit surprised about the list and map support in groovy. Maybe I am missing something here.
I am used to convert between strings, lists and arrays/maps in tcl on the fly. In tcl, something like
"['a':2,'b':4]".each {key, value -> println key + " " + value}
would be possible, where as in groovy, the each command steps through each character of the string.
This would be much of a problem is I could easily use something like the split or tokenize command, but because a serialized list or map isn't just "a:2,b:4", it is a little bit harder to parse.
It seems that griffon developers use a stringToMap library (http://code.google.com/p/stringtomap/) but the example can't cope with the serialized maps either.
So my question is now: what's the best way to parse a map or a list in groovy?
Cheers,
Ralf
PS: it's a groovy question, but I've tagged it with grails, because I need this functionality for grails where I would like to pass maps through the URL
Update: This is still an open question for me... so here are some updates for those who have the same problem:
when you turn a Map into a String, a .toString() will result in something which can't be turned back into a map in all cases, but an .inspect() will give you a String which can be evaluated back to a map!
in Grails, there is a .encodeAsJSON() and JSON.parse(String) - both work great, but I haven't checked out yet what the parser will do with JSON functions (possible security problem)
You might want to try a few of your scenarios using evaluate, it might do what you are looking for.
def stringMap = "['a':2,'b':4]"
def map = evaluate(stringMap)
assert map.a == 2
assert map.b == 4
def stringMapNested = "['foo':'bar', baz:['alpha':'beta']]"
def map2 = evaluate(stringMapNested)
assert map2.foo == "bar"
assert map2.baz.alpha == "beta"
Not exactly native groovy, but useful for serializing to JSON:
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
def map = ['a':2,'b':4 ]
def s = new JsonBuilder(map).toString()
println s
assert map == new JsonSlurper().parseText(s)
with meta-programming:
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
Map.metaClass.toJson = { new JsonBuilder(delegate).toString() }
String.metaClass.toMap = { new JsonSlurper().parseText(delegate) }
def map = ['a':2,'b':4 ]
assert map.toJson() == '{"a":2,"b":4}'
assert map.toJson().toMap() == map
unfortunately, it's not possible to override the toString() method...
I think you are looking for a combination of ConfigObject and ConfigSlurper. Something like this would do the trick.
def foo = new ConfigObject()
foo.bar = [ 'a' : 2, 'b' : 4 ]
// we need to serialize it
new File( 'serialized.groovy' ).withWriter{ writer ->
foo.writeTo( writer )
}
def config = new ConfigSlurper().parse(new File('serialized.groovy').toURL())
// highest level structure is a map ["bar":...], that's why we need one loop more
config.each { _,v ->
v.each {key, value -> println key + " " + value}
}
If you don't want to use evaluate(), do instead:
def stringMap = "['a':2,'b':4]"
stringMap = stringMap.replaceAll('\\[|\\]','')
def newMap = [:]
stringMap.tokenize(',').each {
kvTuple = it.tokenize(':')
newMap[kvTuple[0]] = kvTuple[1]
}
println newMap
I hope this help:
foo= "['a':2,'b':4]"
Map mapResult=[:]
mapResult += foo.replaceAll('\\[|\\]', '').split(',').collectEntries { entry ->
def pair = entry.split(':')
[(pair.first().trim()): pair.last().trim()]
}
def l = ["My", "Homer"]
String s = "Hi My Name is Homer"
def list = s.split(" ")
println list
list.each{it ->
l.each{it1 ->
if (it == it1)
println "found ${it}"
}
}
I want to check whether big list (list) contains all elements of sublist (l)
Does groovy have any built in methods to check this or what I have in the above code will do?
You could use Groovy's Collection.intersect(Collection right) method and check whether the returned Collection is as big as the one that's passed as argument.
You have to use the String.tokenize() method before to generate a List from the String instead of String.split() which returns a String array:
def sublist = ["My", "Homer"]
def list = "Hi My Name is Homer".tokenize()
assert sublist.size() == list.intersect(sublist).size()
Alternatively, you could use Groovy's Object.every(Closure closure) method and check if each element of the sublist is contained in the list:
assert sublist.every { list.contains(it) }
However, the shortest way is using the standard Java Collection API:
assert list.containsAll(sublist)
The easiest method is just to call:
list.containsAll(l)
You can find more information about it here: Groovy Collections
Your solution will work. Be sure to consider the Knuth–Morris–Pratt algorithm if you're dealing with large arrays of relatively few discrete values.