Here is the code i am trying to get working
def expr = ''
List params = []
params << 'filter-name'
params << 'servlet-name'
params << 'url-pattern'
params.each{expr = expr+ "it.'${it}'.text().trim()#"}
expr = expr.substring(0, expr.length()-1)
consNodes.each{
println "data is:$"{expr}"
println "actual : ${it.'filter-name'.text().trim()}#${it.'servlet-name'.text().trim()}#${it.'url-pattern'.text().trim()}"
}
in the above result comes like
data is:it.'filter-name'.text().trim()#it.'servlet-name'.text().trim()#it.'url-pattern'.text().trim()
actual : presenceLogoutFilter##/adfAuthentication/*
data is:it.'filter-name'.text().trim()#it.'servlet-name'.text().trim()#it.'url-pattern'.text().trim()
actual : remoteApplication##/rr/*
data is:it.'filter-name'.text().trim()#it.'servlet-name'.text().trim()#it.'url-pattern'.text().trim()
actual : ServletADFContextFilter#GetHandler#
data is:it.'filter-name'.text().trim()#it.'servlet-name'.text().trim()#it.'url-pattern'.text().trim()
actual : ServletADFContextFilter##/PresenceServlet/*
So, as you can see that my constructed expression is not able to evaluate further. Any advise on how to make it work?
The problem is that you're creating a GString like this: "it.'${it}'.text().trim()#", but then you're converting it into a String when you concatenate it: expr + "it.'${it}'.text().trim()#". Once you turn a GString into a String it no longer evaluates expressions. But even if you address that it won't solve your problem because GStrings do not evaluate like you think they do. The best way to explain it is with an example:
import org.codehaus.groovy.runtime.GStringImpl
// What you're doing
def a = 'John'
def b = 'Hello, $a'
def c = "${a}"
assert b == 'Hello, $a'
// What the compiler is doing (ignoring the variable name changes)
def aa = 'John'
def bb = 'Hello, $a'
def cc = new GStringImpl([] as Object[], ['Hello, $a'] as String[])
assert cc == 'Hello, $a'
// What you want the compiler to do, but it will not.
def aaa = 'John'
def bbb = 'Hello, $a'
def ccc = new GStringImpl(['John'] as Object[], ['Hello, '] as String[])
assert ccc == 'Hello, John'
If you really want to, you can build the GStrings manually, but that will be very difficult. And you'd end up depending on a class which is not guaranteed to remain backward-compatible between Groovy releases.
Here's what you can do instead:
def params = []
params << 'filter-name'
params << 'servlet-name'
params << 'url-pattern'
def evaluators = params.collect {
{ attr, node -> node[attr]?.text()?.trim() ?: '' }.curry(it)
}
consNodes.each { node ->
println evaluators.collect { c -> c(node) }.join('#')
}
The output looks like this:
presenceLogoutFilter##/adfAuthentication/*
remoteApplication##/rr/*
ServletADFContextFilter#GetHandler#
ServletADFContextFilter##/PresenceServlet/*
Instead of a single large expression you and up with a list of closures, each responsible for evaluating a node attribute. Then, you can join the results with '#'s.
Related
So i'm trying to convert python to lua but a road block i have right now is the class i'm not sure how i would convert a class to lua does anyone know how i would go about converting a class from python to lua?
python class
class
The OOP magic can be done with Lua tables and metatables.
Python
Look at The __init__() Function example here...
https://www.w3schools.com/python/python_classes.asp
Lets implement this with Lua...
( Lua 5.4 interactive console )
> _VERSION
Lua 5.4
> Person = setmetatable({},{
__call = function(self, name, age)
self.name = name
self.age = age
return {name = self.name, age = self.age}
end
})
> p1 = Person("John", 36)
> print(p1.name, p1.age)
John 36
So, some readers would ask: Why a metatabled table?
A simple function could do the same job.
Once a table has a metatable it is quit simple to add methods...
> Person = setmetatable({},{
__call = function(self, name, age)
self.name, self.age = name, age
table.insert(self, {name = self.name, age = self.age}) -- Numbered keys are the Data part
return {name = self.name, age = self.age}
end,
__index = {list = function(self) for i = 1, #self do print(self[i].name, self[i].age) end end}
})
> Person("John", 31);
> Person("Jack", 32);
> Person("Jim", 33);
> Person:list()
John 31
Jack 32
Jim 33
...that can handle the data of the table by itself.
Its a great benefit that the behaviour of a table can be controlled with metatables/methods.
And above example is only the tip of the iceberg.
Have a look at the __index of a string...
> for key, value in pairs(getmetatable(_VERSION).__index) do print(key, "=", value) end
byte = function: 0x565d6f20
lower = function: 0x565d4d90
len = function: 0x565d4750
sub = function: 0x565d7210
dump = function: 0x565d5d00
gsub = function: 0x565d7dc0
char = function: 0x565d5060
unpack = function: 0x565d6530
match = function: 0x565d7da0
packsize = function: 0x565d6420
pack = function: 0x565d6950
upper = function: 0x565d4ac0
format = function: 0x565d52d0
reverse = function: 0x565d4b50
find = function: 0x565d7db0
gmatch = function: 0x565d70d0
rep = function: 0x565d4be0
Therefore this is possible...
> print(("koyaanisqatsi"):upper():reverse())
ISTAQSINAAYOK
Means: Methods can be chained if their returning datatype has a method
Exceptions are: len() accept but dont return a string and sub() needs numbers before it returns a string also dump() needs a self defined function and returns a binary (almost unreadable) string.
This is my simple groovy script;
def fourtify(String str) {
def clsr = {
str*4
}
return clsr
}
def c = fourtify("aa")
println("binding variables: ${c.getBinding().getVariables()}")
...
All I'm trying to do here is being able to access the free variable "str" using the closure instance to understand how closure works behind the scenes a bit more better. Like, perhaps, Python's locals() method.
Is there a way to do this?
The closure you have defined does not store anything in binding object - it simply returns String passed as str variable, repeated 4 times.
This binding object stores all variables that were defined without specifying their types or using def keyword. It is done via Groovy metaprogramming feature (getProperty and setProperty methods to be more specific). So when you define a variable s like:
def clsr = {
s = str*4
return s
}
then this closure will create a binding with key s and value evaluated from expression str * 4. This binding object is nothing else than a map that is accessed via getProperty and setProperty method. So when Groovy executes s = str * 4 it calls setProperty('s', str * 4) because variable/property s is not defined. If we make a slightly simple change like:
def clsr = {
def s = str*4 // or String s = str * 4
return s
}
then binding s won't be created, because setProperty method does not get executed.
Another comment to your example. If you want to see anything in binding object, you need to call returned closure. In example you have shown above the closure gets returned, but it never gets called. If you do:
def c = fourtify("aa")
c.call()
println("binding variables: ${c.getBinding().getVariables()}")
then your closure gets called and binding object will contain bindings (if any set). Now, if you modify your example to something like this:
def fourtify(String str) {
def clsr = {
def n = 4 // it does not get stored as binding
s = str * n
return s
}
return clsr
}
def c = fourtify("aa")
c.call()
println("binding variables: ${c.getBinding().getVariables()}")
you will see following output in return:
binding variables: [args:[], s:aaaaaaaa]
Hope it helps.
in your example str is a parameter of the method/function fortify
however maybe following example will give you better Closure understanding:
def c={ String s,int x-> return s*x }
println( c.getClass().getSuperclass() ) // groovy.lang.Closure
println( c.getMaximumNumberOfParameters() ) // 2
println( c.getParameterTypes() ) // [class java.lang.String, int]
the locals() Python's function better matches groovy.lang.Script.getBinding()
and here is a simple example with script:
Script scr = new GroovyShell().parse('''
println this.getBinding().getVariables() // print "s" and "x"
z = s*(x+1) // declare a new script-level var "z"
println this.getBinding().getVariables() // print "s", "x", and "z"
return s*x
''')
scr.setBinding( new Binding([
"s":"ab",
"x":4
]) )
println scr.run() // abababab
println scr.getBinding().getVariables() // print "s", "x", and "z"
I want convert string to Map in grails. I already have a function of string to map conversion. Heres the code,
static def StringToMap(String reportValues){
Map result=[:]
result=reportValues.replace('[','').replace(']','').replace(' ','').split(',').inject([:]){map,token ->
List tokenizeStr=token.split(':');
tokenizeStr.size()>1?tokenizeStr?.with {map[it[0]?.toString()?.trim()]=it[1]?.toString()?.trim()}:tokenizeStr?.with {map[it[0]?.toString()?.trim()]=''}
map
}
return result
}
But, I have String with comma in the values, so the above function doesn't work for me. Heres my String
[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]
my function returns ABC only. not ABC, INC. I googled about it but couldnt find any concrete help.
Generally speaking, if I have to convert a Stringified Map to a Map object I try to make use of Eval.me. Your example String though isn't quite right to do so, if you had the following it would "just work":
// Note I have added '' around the values.
String a = "[program_type:'', subsidiary_code:'', groupName:'', termination_date:'', effective_date:'', subsidiary_name:'ABC']"
Map b = Eval.me(a)
// returns b = [program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC]
If you have control of the String then if you can create it following this kind of pattern, it would be the easiest solution I suspect.
In case it is not possible to change the input parameter, this might be a not so clean and not so short option. It relies on the colon instead of comma values.
String reportValues = "[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
reportValues = reportValues[1..-2]
def m = reportValues.split(":")
def map = [:]
def length = m.size()
m.eachWithIndex { v, i ->
if(i != 0) {
List l = m[i].split(",")
if (i == length-1) {
map.put(m[i-1].split(",")[-1], l.join(","))
} else {
map.put(m[i-1].split(",")[-1], l[0..-2].join(","))
}
}
}
map.each {key, value -> println "key: " + key + " value: " + value}
BTW: Only use eval on trusted input, AFAIK it executes everything.
You could try messing around with this bit of code:
String tempString = "[program_type:11, 'aa':'bb', subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
List StringasList = tempString.tokenize('[],')
def finalMap=[:]
StringasList?.each { e->
def f = e?.split(':')
finalMap."${f[0]}"= f.size()>1 ? f[1] : null
}
println """-- tempString: ${tempString.getClass()} StringasList: ${StringasList.getClass()}
finalMap: ${finalMap.getClass()} \n Results\n finalMap ${finalMap}
"""
Above produces:
-- tempString: class java.lang.String StringasList: class java.util.ArrayList
finalMap: class java.util.LinkedHashMap
Results
finalMap [program_type:11, 'aa':'bb', subsidiary_code:null, groupName:null, termination_date:null, effective_date:null, subsidiary_name:ABC, INC:null]
It tokenizes the String then converts ArrayList by iterating through the list and passing each one again split against : into a map. It also has to check to ensure the size is greater than 1 otherwise it will break on f[1]
Good old java way to conditionally append something to string is as follows:
if (booleanFlag) {
myString += "something to append"
}
Can I do the same in more groovy way, ideally in one line?
A very Groovy way to do this would be with GStrings:
"$myString${booleanFlag ? 'something to append' : ''}"
Here is a groovy way using GString closures:
>>> def world = false
>>> def people = true
>>>
>>> def message = "Hello${sw -> if (world) sw << ' World'; if (people) sw << ' People'}"
>>>
>>> message
Hello People
>>>
>>> people = false
>>> world = true
>>>
>>> message
Hello World
>>>
>>> world = false
>>> message
Hello
The string looks kinda long and could do with some indentation, but groovy shell did not allow me to split the lines. Switched to an IDE, turns out you can write the string better like this (with the help of triple quote strings):
def message =
"""Hello${sw ->
if (false) sw << ' World!'
if (false) sw << ' People!'
if (true) sw << ' Groovy!'
}"""
Now that's groovy!
Try:
def b = true
def s = 's'
s += b ? 's' : ''
Here's a solution creating a String.metaClass.when method:
String.metaClass.when = { it ? delegate : '' }
Testing:
flag = true
myString = 'foo '
myString += "to append".when flag
assert myString == 'foo to append'
myString = 'foo ' + "to append".when(false)
assert myString == 'foo '
I think this pattern is a good candidate for some meta-programming.
def myString = 'Hello'
use(StringBuilderCategory) {
assert new StringBuilder(myString).append(true, 'World').toString() == 'HelloWorld'
assert new StringBuilder(myString).append(false, 'World').toString() == 'Hello'
}
class StringBuilderCategory {
static StringBuilder append(StringBuilder builder, boolean condition, String str) {
if(condition) {
builder.append(str)
} else {
builder
}
}
}
I used a StringBuilder to avoid implying that Strings are mutable, but a similar method can be added to String to get it down to this:
use(TheCategory) {
myString = myString.append(booleanFlag, 'something to append')
}
Of course there's the option of using the meta class instead of a category.
Looks like one line to me:
if (booleanFlag) myString += "something to append";
I have a groovy script with an unknown number of variables in context at runtime, how do I find them all and print the name and value of each?
Well, if you're using a simple script (where you don't use the "def" keyword), the variables you define will be stored in the binding and you can get at them like this:
foo = "abc"
bar = "def"
if (true) {
baz = "ghi"
this.binding.variables.each {k,v -> println "$k = $v"}
}
Prints:
foo = abc
baz = ghi
args = {}
bar = def
I'm not aware of an easy way to enumerate through the variables defined with the "def" keyword, but I'll be watching this question with interest to see if someone else knows how.
Actually, Ted's answer will also work for 'def'ed variables.
def foo = "abc"
def bar = "def"
if (true) {
baz = "ghi"
this.binding.variables.each {k,v -> println "$k = $v"}
}
yields
baz = ghi
__ = [null, null, null]
foo = abc
_ = null
bar = def
I'm not sure what the _-variables signify, but I'm sure you can work around them.
Groovy Object contains method - dump()
https://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Object.html
String dump()
Generates a detailed dump string of an object showing its class, hashCode and fields.
Example:
class MyClass {
def foo = "abc"
def bar = "def"
}
println(new MyClass().dump())
stdout: <MyClass#1fa268de foo=abc bar=def>