How to instantiate a class using an explicit first, and a default second argument, via the meta model? - metaprogramming

I have the following toplevel class:
class Example(shared String first = "default one", shared String second = "default two") {
}
Now, I want to instantiate this class using an explicit value for first, but the default value for second.
I know how to do this via explicitly compiled code, by using named arguments:
void instantiateExample(String firstValue) {
Example ex = Example { first = firstValue; };
assert(ex.first == firstValue);
assert(ex.second == "default two");
}
Now, I would like to do the same thing as the above code, but by using the Class<Example, []|[String,String=]> object.

with class model it is ...
Class<Example,[]|[String, String=]> exampleModel = `Example`;
value e1 = exampleModel();
value e2 = exampleModel("foo");
value e3 = exampleModel("foo", "bar");
or with class declaration it is ...
ClassDeclaration exampleDeclaration = `class Example`;
assert(is Example e1 = exampleDeclaration.instantiate());
assert(is Example e2 = exampleDeclaration.instantiate([], "foo"));
assert(is Example e3 = exampleDeclaration.instantiate([], "foo", "bar"));
HTH

Related

How to Use Groovy to find the INTERSECTION of two lists?

I am trying to retrieve the common items across two lists using Groovy. The following code works just fine, i.e the output from running this code is "DEBUG found in common Items : same". So far so good!
def list1 = ["same", "different"]
def list2 = ["same", "not the same"]
def commonItems = list1.intersect(list2)
for(int i=0; i < commonItems.size(); i++)
{
log.info("DEBUG found in common Items : " + commonItems[i])
}
I hit an issue when I try to apply the above principle to a list of objects - my issue is that the 'commonItems' list does NOT contain the single object I would expect, but is empty. Please note, my custom object 'ErrorWarningDetail' does override compareTo. Can someone see what I am doing wrong / make any suggestions? Thanks in advance!
First of all here is my custom class - note 'CompateTo' just checks the 'Code' field for now.
class ErrorWarningDetail implements Comparable
{
public String Code
public String Description
public String ErrorType
public String State
#Override
int compareTo(Object o) {
int result = Code <=> o.Code
result
}
}
Now here is the code that does the business. I would expect one object to be in 'commonItems' but it is infact empty - what am i doing wrong here? The output of running this is "DEBUG no common items"
def similarONE = new ErrorWarningDetail()
similarONE.Code = "100-1"
def similarTWO =new ErrorWarningDetail()
similarTWO.Code = "100-1"
def completelyDifferent = new ErrorWarningDetail()
completelyDifferent.Code = "697-2"
def List1 = []
def List2 = []
List1.add(similarONE)
List1.add(completelyDifferent)
List2.add(similarTwo)
def commonItems = list1.intersect(list2)
if (commonItems.size() == 0)
{
log.info("DEBUG no common items")
}
Implementing compareTo() is not enough in Java, you should be implementing equals/hashCode instead.
In Groovy there's a handy annotation for that. So, the script down below executes successfully:
import groovy.transform.EqualsAndHashCode
#EqualsAndHashCode( includes = [ 'code' ] )
class ErrorWarningDetail implements Comparable {
String code
String description
String errorType
String state
#Override
int compareTo(Object o) {
code <=> ( o?.code ?: '' )
}
}
def similarONE = new ErrorWarningDetail( code:"100-1" )
def similarTWO = new ErrorWarningDetail( code:"100-1" )
def completelyDifferent = new ErrorWarningDetail( code:"697-2" )
def list1 = [similarONE, completelyDifferent]
def list2 = [similarTWO]
def commonItems = list1.intersect list2
assert 1 == commonItems.size()
P.S. Please, DO NOT name fields starting with Capital letters!
The equals and hashCode are the methods utilized to determine object equality, so the intersect method would rely on those.
The compareTo method is utilized for sorting purposes.
Groovy has some convenient utilities for common tasks in the package groovy.transform
Below is the modified class with the annotations that makes it work as intended.
#EqualsAndHashCode(includes=["Code"])
#ToString(includeFields=true)
class ErrorWarningDetail implements Comparable
{
String Code
String Description
String ErrorType
String State
#Override
int compareTo(Object o) {
Code <=> o?.Code
}
}

Retrofit skips init { } block of a data class while initializing

I'm going to set variables in class Data based on the arguments of DClass
abstract class Data(p: String) {
var a1: String
var a2: String
init {
"""(\w+)(\d+)""".toRegex().find(p)!!.groupValues.run {
a1 = "a1 is ${get(1)}"
a2 = "a2 is ${get(2)}"
}
}
}
data class DClass(val p1: String, val p2: String) : Data(p1)
Then I'm able to get values of a1 and a2 after DClass is created:
DClass("string1", "string2").run { println("$a1 $a2") }
it returns "a1 is string a2 is 1" as it should
Next I'm trying to make retrofit initialize DClass from the proper JSON response:
#GET("loadDClass") suspend fun dClass(): DClass
and say the program to do the same output of a1 and a2, it just returns:
"a1 is null a2 is null".
So I found out that init{} block of Data is skipped as the retrofit just builds DClass but doesn't do the initialization
Is it possible to make abstract class' variables be initialized without doing it manually?
The init block is not called because GsonConverterFactory (which I assume you use since it is very common in this case) constructs the DClass in a special way that does not invoke its constructor. In particular, an internal class called UnsafeAllocator is used. The JavaDoc for that class states:
/**
* Do sneaky things to allocate objects without invoking their constructors.
* [...]
*/
You could write your own Retrofit Converter.Factory but that would IMO be overkill. I suggest you add a simple member function to DClass instead of inheriting from Data, and also remove the abstract keyword from Data. DClass should look like this:
data class DClass(val p1: String, val p2: String)
fun newData(): Data = Data(p1)
}
then you can construct a Data instance from the body() of the response:
call.execute().body()?.newData()?.run { println("$a1 $a2") }

Could I combine two closures in groovy?

I have one closure:
def Boolean check(String name, String value, Context c){
c.name.equalsIgnoreCase(value);
}
There is a get method to get the closure:
def getClosure() {
check
}
I am trying to get the Logical disjunction of two calls (name could be "Dog" or "Cat"):
c1 = check.curry("name", "Dog");
c2 = check.curry("name", "Cat");
c3 = c1 || c2; /*Could this closure be created?*/
Context ctx = new Context();
c3.call(ctx);
My question is is there a way to create a c3 closure?
Thanks!
I don't think that Groovy ships with a combinator for or'ing predicates (if that's what you are asking). You'll probably have to do c3 = { c1(it) || c2(it) }.

Does Groovy #Canonical work with private fields?

I keep getting a "Could not find matching constructor" when I use #Canonical annotation with private fields. May I know how I can get the following code to compile and pass the asserts? Thanks :)
#groovy.transform.Canonical(includes = ['x','y'])
#groovy.transform.EqualsAndHashCode(includeFields = true)
class Cell implements Comparable<Cell>{
private int x
private int y
int compareTo(Cell cell){
x <=> cell.x ?: y <=> cell.y ?: 0
}
}
Cell cell = new Cell(1,0);
Cell sameCell = new Cell(1,0);
def setOfLiveCells = [] as Set
assert setOfLiveCells.add(cell) // cell is added
assert cell == sameCell // cell equals
assert ! setOfLiveCells.add(sameCell) //should not assert
Regards,
John
Yes, you can do that.
#Canonical is a shorthand for #TupleConstructor #ToString and #EqualsAndHashCode annotations with default values. [code] You can specify them by hand, or simply add necessary annotation before Canonical. #TupleConstructor allows you to set fields, via includeFields or include annotation fields. [doc]
#groovy.transform.TupleConstructor(includeFields = true)
#groovy.transform.Canonical
class Cell implements Comparable<Cell>{
private int x
private int y
int compareTo(Cell cell){
x <=> cell.x ?: y <=> cell.y ?: 0
}
}
Cell cell = new Cell(1,0);
Cell sameCell = new Cell(1,0);
def setOfLiveCells = [] as Set
assert setOfLiveCells.add(cell) // cell is added
assert cell == sameCell // cell equals
assert ! setOfLiveCells.add(sameCell) //should not assert

In order for a groovy closure to modify a variable defined in the scope of a delegate, do you need to explicitly specify delegate.theVariableName?

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.

Resources