I'm trying to create a script which gathers puts elements from XML in a list and verifies that a specific object is a part of this list. Please find my script below:
class BaggageEntitlement{
String allowancePieces
String ruleType
String ruleId
String passengerNameRef
String segmentNumber
boolean equals(BaggageEntitlement that) {
return (
this.allowancePieces.equals(that.allowancePieces) &&
this.ruleType.equals(that.ruleType) &&
this.ruleId.equals(that.ruleId) &&
this.passengerNameRef.equals(that.passengerNameRef) &&
this.segmentNumber.equals(that.segmentNumber)
)
}
}
ArrayList<BaggageEntitlement> expectedEntitlements = new ArrayList<BaggageEntitlement>()
ArrayList<BaggageEntitlement> actualEntitlements = new ArrayList<BaggageEntitlement>()
BaggageEntitlement segment1Entitlement = new BaggageEntitlement(allowancePieces : '2', ruleType : 'OVERRIDE', ruleId : '15483', passengerNameRef: '01.01', segmentNumber: '1')
def cbfNode = new XmlSlurper().parseText(messageExchange.getResponseContentAsXml()).Body.CalculateBagFeesRS
def baggageEntitlementNode = cbfNode.AncillaryOffers.Itinerary.BaggageEntitlements
baggageEntitlementNode.EntitlementItineraryPart.each{
def baggageAllowanceEntitlementNode = it.BaggageAllowanceEntitlement
BaggageEntitlement baggageEntitlement = new BaggageEntitlement()
baggageEntitlement.allowancePieces = baggageAllowanceEntitlementNode.MaxPieces.text().toString()
baggageEntitlement.ruleType = baggageAllowanceEntitlementNode.#ruleType.toString()
baggageEntitlement.ruleId = baggageAllowanceEntitlementNode.#ruleId.toString()
baggageEntitlement.passengerNameRef = it.PassengerReference.#nameReferenceNumber.toString()
baggageEntitlement.segmentNumber = airIdToSegmentNumber[it.SegmentReference.#segmentNumber.toString()]
actualEntitlements.add(baggageEntitlement)
}
But after creating a collection, objects which are present in created collection aren't recognized as a part of collection. I've created the following piece of code to demonstrate this:
println "Is iterator in collection: ${actualEntitlements.contains(segment1Entitlement)}"
println "Is object equals (I) to iterator: ${actualEntitlements[0] == segment1Entitlement}"
println "Is object equals (II) to iterator: " + actualEntitlements[0].equals(segment1Entitlement)
println "Is 'allowancePieces' members are equal: " + (actualEntitlements[0].allowancePieces == segment1Entitlement.allowancePieces)
println "Is 'ruleType' members are equal: " + (actualEntitlements[0].ruleType == segment1Entitlement.ruleType)
println "Is 'ruleId' members are equal: " + (actualEntitlements[0].ruleId == segment1Entitlement.ruleId)
println "Is 'passengerNameRef' members are equal: " + (actualEntitlements[0].passengerNameRef == segment1Entitlement.passengerNameRef)
println "Is 'segmentNumber' members are equal: " + (actualEntitlements[0].segmentNumber == segment1Entitlement.segmentNumber)
And the console output for comparing object member by member is looks like:
Is iterator in collection: false
Is object equals (I) to iterator: true
Is object equals (II) to iterator: true
Is 'allowancePieces' members are equal: true
Is 'ruleType' members are equal: true
Is 'ruleId' members are equal: true
Is 'passengerNameRef' members are equal: true
Is 'segmentNumber' members are equal: true
Could you tell me why my object isn't considered to be a part of collection though member by member comparison returns 'true' for every member?
Because you did not override equals. You supplied your own equals method with a different signature. Override the correct equals method...
public boolean equals(Object o) {
... your code here...
}
...and the Collection will recognize your object, as by the Collection contract for Collection.contains(...)
Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)).
(This means the correct equals method and not any equals method. The parameter must be an object, otherwise it is a different equals method and will not help you.)
Having a convenient method like boolean equals(BaggageEntitlement that) may be nice for other purposes (though I doubt it), but this method won't be called since most other classes will expect an Object and not a "BaggageEntitlement". Equals is one of the basic methods of Object, so you should always override that instead of creating your own different version.
Related
As part of learning Groovy, I'm trying to explore all intricate possibilities provided by string interpolation.
One of my little experiments gave results that don't make sense to me, and now I'm wondering whether I've completely misunderstood the basic concepts of lazy and eager interpolation in Groovy.
Here's the code I ran:
def myVar1 = 3
// An eager interpolation containing just a closure.
def myStr = "${{->myVar1}}"
print ("Just after the creation of myStr\n")
print (myStr as String)
myVar1 += 1 // Bump up myVar1.
print ("\nJust after incrementing myVar1\n")
print (myStr as String)
Here's the output I got:
Just after the creation of myStr
3
Just after incrementing myVar1
4
Clearly, the closure has been invoked a second time. And the only way the closure could have been re-executed is by the containing interpolation getting re-evaluated. But then, the containing interpolation is, by itself, not a closure, though it contains a closure. So then, why is it getting re-evaluated?
This is how GString.toString() method is implemented. If you take a look at the source code of GString class, you will find something like this:
public String toString() {
StringWriter buffer = new StringWriter();
try {
writeTo(buffer);
}
catch (IOException e) {
throw new StringWriterIOException(e);
}
return buffer.toString();
}
public Writer writeTo(Writer out) throws IOException {
String[] s = getStrings();
int numberOfValues = values.length;
for (int i = 0, size = s.length; i < size; i++) {
out.write(s[i]);
if (i < numberOfValues) {
final Object value = values[i];
if (value instanceof Closure) {
final Closure c = (Closure) value;
if (c.getMaximumNumberOfParameters() == 0) {
InvokerHelper.write(out, c.call());
} else if (c.getMaximumNumberOfParameters() == 1) {
c.call(out);
} else {
throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
+ c.getMaximumNumberOfParameters() + " parameters");
}
} else {
InvokerHelper.write(out, value);
}
}
}
return out;
}
Notice that writeTo method examines what the values passed for interpolation are, and in case of closure, it invokes it. This is the way GString handles lazy-evaluation of interpolated values.
Now let's take a look at a few examples. Let's assume we want to print a GString and interpolate a value returned by some method call. This method will also print something to the console, so we can see if the method call was triggered eagerly or lazily.
Ex.1: Eager evaluation
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def str = "${loadValue(var++)}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
This method returns value 1
Starting the loop...
1
1
1
1
1
Loop ended...
The default eager behavior. The method loadValue() was invoked before we have printed out str to the console.
Ex.2: Lazy evaluation
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def str = "${ -> loadValue(var++)}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
Starting the loop...
This method returns value 1
1
This method returns value 2
2
This method returns value 3
3
This method returns value 4
4
This method returns value 5
5
Loop ended...
In the second example, we take advantage of lazy evaluation. We define str with a closure that invokes loadValue() method and this invocation is executed when we explicitly print the str to the console (to be more specific - when the GString.toString() method gets executed).
Ex.3: Lazy evaluation and closure memoization
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def closure = { -> loadValue(var++)}
def str = "${closure.memoize()}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
Starting the loop...
This method returns value 1
1
1
1
1
1
Loop ended...
And here is the example you most probably look for. In this example, we still take advantage of lazy evaluation thanks to the closure parameter. However, in this case, we use closure's memoization feature. The evaluation of the string is postponed to the first GString.toString() invocation and closure's result gets memorized, so the next time it gets called it returns the result instead of re-evaluating the closure.
What is the difference between ${{->myVar1}} and ${->myVar1}?
As it was mentioned earlier, GString.toString() method uses GString.writeTo(out) that checks if the given placeholder stores a closure for a lazy evaluation. Every GString instance store placeholder values in the GString.values array and it gets initialized during GString initialization. Let's consider the following example:
def str = "${myVar1} ... ${-> myVar1} ... ${{-> myVar1}}"
Now let's follow GString.values array initialization:
${myVar1} --> evaluates `myVar1` expression and copies its return value to the values array
${-> myVar1} --> it sees this is closure expression so it copies the closure to values array
${{-> myVar1}} --> evaluates `{-> myVar1}` which is closure definition expression in this case and copies its return value (a closure) to the values array
As you can see, in the 1st and 3rd example it did exactly the same - it evaluated the expression and stored it in the GString.values array of type Object[]. And here is the crucial part: the expression like {->something} is not a closure invocation expression. The expression that evaluates the closure is
{->myVar1}()
or
{->myVar1}.call()
It can be illustrated with the following example:
def str = "${println 'B'; 2 * 4} ${{ -> println 'C'; 2 * 5}} ${{ -> println 'A'; 2 * 6}.call()}"
println str
Values initialization is as follows:
${println 'B'; 2 * 4} ---> evaluates the expression which prints 'B' and returns 8 - this value is stored in values array.
${{ -> println 'C'; 2 * 5}} ---> evaluates the expression which is nothing else than creation of a closure. This closure is stored in the values array.
${{ -> println 'A'; 2 * 6}.call()}" ---> evaluates the expression which creates a closure and then calls it explicitely. It prints 'A' and returns 12 which is stored in the values array at the last index.
That is why after GString object initialization we end up with values array like:
[8, script$_main_closure1, 12]
Now, the creation of this GString caused a side effect - the following characters shown on the console:
B
A
This is because the 1st and the 3rd values evaluation invoked println method call.
Now, when we finally call println str which invokes GString.toString() method, all values get processed. When the interpolation process starts it does the following:
value[0] --> 8 --> writes "8"
value[1] --> script$_main_closure1 --> invoke script$_main_closure1.call() --> prints 'C' --> returns 10 --> 10 --> writes "10"
value[2] --> 12 --> writes "12"
That is why the final console output looks like this:
B
A
C
8 10 12
This is why in practice expressions like ${->myVar1} and ${{->myVar1}} are similar. In the first case GString initialization does not evaluate the closure expression and puts it directly to the values array, in the second example placeholder gets evaluated and the expression it evalutes creates and returns the closure which then gets stored in the values array.
Note on Groovy 3.x
If you try to execute the expression ${{->myVar1}} in Groovy 3.x you will end up with the following compiler error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during conversion: java.lang.NullPointerException
java.lang.NullPointerException
at org.apache.groovy.parser.antlr4.AstBuilder.lambda$visitGstring$28(AstBuilder.java:3579)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at org.apache.groovy.parser.antlr4.AstBuilder.visitGstring(AstBuilder.java:3591)
at org.apache.groovy.parser.antlr4.AstBuilder.visitGstring(AstBuilder.java:356)
at org.apache.groovy.parser.antlr4.GroovyParser$GstringContext.accept(GroovyParser.java:4182)
at groovyjarjarantlr4.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:20)
at org.apache.groovy.parser.antlr4.AstBuilder.visit(AstBuilder.java:4287)
.....
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:565)
at org.codehaus.groovy.tools.FileSystemCompiler.compile(FileSystemCompiler.java:72)
at org.codehaus.groovy.tools.FileSystemCompiler.doCompilation(FileSystemCompiler.java:240)
at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompile(FileSystemCompiler.java:163)
at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompileWithErrorHandling(FileSystemCompiler.java:203)
at org.codehaus.groovy.tools.FileSystemCompiler.main(FileSystemCompiler.java:187)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:114)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:136)
1 error
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]
I'm trying to get the first K elements from a sorted map by the following piece of code:
//return top rank k elements
public static LinkedHashMap<String,Double> getTopRank(int i){
//store top k elements
LinkedHashMap<String, Double> result=new LinkedHashMap<>();
int count=0;
//use the static rankMap in the class
rankMap.each {key,value->
result.put(key, value);
count++;
if(count>=i){
println "Time to return"
return result;
}
}
//in case the loop does not work
return result;
}
What I expect is that when the result Map already has a size of i elements, the method will return, giving me a i-size sorted map. Note that rankMap stores the elements in a certain order I want, and its size is far bigger than int i I pass to the method.
And I'm calling the method by
LinkedHashMap<String,Double> content=getTopRank(outputSize);
But unexpectedly finally the content had the size of rankMap rather than i! And in the console I saw hundreds of Time to return lines. The line return result was executed again and again until it reached the end of rankMap.
I'm pretty sure that the line getTopRank(outputSize) was not in a loop. Then it seems strange to me why this method can return multiple times without ending. Is it caused by my putting return statement in the closure?
Please advise or tell me how this is true in Groovy. One step further, how can I get only first k elements from a sorted map then?
You misunderstood key concepts of Groovy.
The only way to finish the each() execution before reaching the end, is to throw an exception. If your want to exit the loop conditionally, use standard loop types like for or while:
int count=0
def result = [:]
for( def e in rankMap ){
count++
result[ e.key ] = e.value
if( i <= count ) return result
}
The method itself is not returning. each is a method which receives a closure. Closures have their own returning context, which is not tied to the method who invoked them, thus, the loop is not broken.
I'd like to suggest getting a range from the map's entrySet and collecting the resulting entries:
def getTopRank(int i) {
rankMap
.entrySet()
.toList()[0..<i]
.collectEntries()
}
rankMap = [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon',
'Rolling Stones' : 'Sticky Fingers',
'The Doors' : 'Morrison Hotel',
'Bob Dylan' : 'Bob Dylan'
]
assert getTopRank(2) == [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon']
assert getTopRank(4) == [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon',
'Rolling Stones' : 'Sticky Fingers',
'The Doors' : 'Morrison Hotel',]
public static LinkedHashMap<String,Double> getTopRank(int i){
rankMap.take(i)
}
http://www.groovy-lang.org/gdk.html
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html#take(int)
I stumbled onto something with Groovy closures and delegates that I'm not sure is an official part of the language or perhaps even a bug.
Basically I am defining a closure that I read in as a string from an external source,
and one of the variables in the class that defines the closure needs to be modified by the closure. I wrote
a simple example showing what I found works, and what does not work.
If you look at the test code below you will see a class that defines a variable
animal = "cat"
and two closures defined on the fly from strings that attempt to modify the animal variable.
This works >
String code = "{ -> delegate.animal = 'bear'; return name + 'xx' ; }"
But this does not
String code = "{ -> animal = 'bear'; return name + 'xx' ; }"
It seems like I need to explicitly qualify my to-be-modified variable with 'delegate.' for this to work.
(I guess i can also define a setter in the enclosing class for the closure to call to modify the value.)
So.... I've found out how to make this work, but I'd be interested if someone could point me to some groovy
doc that explains the rules behind this.
Specifically.... why will the simple assignment
animal = 'bear'
affect the original variable ? Are there shadow copies being made here or something ?
import org.junit.Test
/*
* Author: cbedford
* Date: 8/30/12
* Time: 1:16 PM
*/
class GroovyTest {
String animal = "cat"
String name = "fred"
#Test
public void testDelegateWithModificationOfDelegateVariable() {
String code = "{ -> delegate.animal = 'bear'; return name + 'xx' ; }"
def shell = new GroovyShell()
def closure = shell.evaluate(code)
closure.delegate = this
def result = closure()
println "result is $result"
println "animal is $animal"
assert animal == 'bear'
assert result == 'fredxx'
}
// This test will FAIL.
#Test
public void testDelegateWithFailedModificationOfDelegateVariable() {
String code = "{ -> animal = 'bear'; return name + 'xx' ; }"
def shell = new GroovyShell()
def closure = shell.evaluate(code)
closure.delegate = this
def result = closure()
println "result is $result"
println "animal is $animal"
assert animal == 'bear'
assert result == 'fredxx'
}
}
Groovy closures have five strategies for resolving symbols inside closures:
OWNER_FIRST: the owner (where the closure is defined) is checked first, then the delegate
OWNER_ONLY: the owner is checked, the delegate is only checked if referenced explicitly
DELEGATE_FIRST: the delegate is checked first, then the owner
DELEGATE_ONLY: the delegate is checked first, the owner is only checked if referenced explicitly
TO_SELF: neither delegate nor owner are checked
The default is OWNER_FIRST. Since the closure is defined dynamically, your owner is a Script object which has special rules itself. Writing animal = 'bear' in a Script will actually create a new binding called animal and assign 'bear' to it.
You can fix your tests to work without explicitly referencing delegate by simply changing the resolve strategy on the closure before calling it with:
closure.resolveStrategy = Closure.DELEGATE_FIRST
This will avoid the odd the Script binding and use the delegate as expected.