Groovy class factory pattern gives weird error - groovy

I am new to groovy and I am trying with some groovy code. I have this scenario. I have the following modules
package com.utils
abstract class Base {
static String data = ''
}
package com.utils
class A extends Base {
static String data = 'dummy'
}
package com.utils
class B extends Base {
static String data = 'dummy'
}
package com.utils
class ShapeFactory {
static Map <String,Object> shapes = [
"a": A,
"b": B
]
static Object get_shapes(String shape) {
return shapes.get(shape);
}
}
And in the main file I am using
and in the main file and here is where it fails with weird error. I couldn't identify the reason, I would appreciate any help.
import com.utils.ShapeFactory
def shapeA = ShapeFactory.get_shapes('a')
shapeA.data // here it fails with the below error
hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException:
Cannot cast object '[]' with class 'java.util.ArrayList' to class 'java.util.Map'
due to: groovy.lang.GroovyRuntimeException:
Could not find matching constructor for: java.util.Map()
Any help will be appreciated, Thanks

Apologies, I found the issue, In the Base class I had a Map variable as well which was initialized as an array
abstract class Base {
static String data = ''
static Map mapper = [] // This has to be [:]
}

Related

Is there a way of getting the names of all subclasses

I want to get the names of all subclasses of a base class. From that I want to #:build an enum where every entry is the name of a class. Is this possible?
Here is the code. The order of macro executions is undefined, as specified in documentation.
[AutoBuildingMacro.hx]
import haxe.macro.Context;
import haxe.macro.Expr;
final mapofnames = new Map<String,Array<String>>();
class AutoBuildingMacro {
public static macro function fromBaseClass(base):Array<Field> {
var parent = haxe.macro.ExprTools.toString(base);
var names = mapofnames[parent];
if ( names == null ) {
names = new Array<String>();
mapofnames[parent] = names;
}
names.push(Context.getLocalClass().toString());
trace(mapofnames);
return null;
}
}
[Main.hx]
import AutoBuildingMacro;
class Main {
static public function main() {}
}
#:autoBuild(AutoBuildingMacro.fromBaseClass(Parent))
class Parent {
}
class Child1 extends Parent {
}
class Child2 extends Parent {
}
class Child3 extends Child2 {
}
Output:
AutoBuildingMacro.hx:15: {Parent => [Child2]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3,Child1]}
You are looking for auto-build macro feature https://haxe.org/manual/macro-auto-build.html
And then enum building https://haxe.org/manual/macro-enum-building.html
I wrote a library called compiletime that has this feature:
// Returns a list of all the classes that inherit MySuperClass, no matter what package
CompileTime.getAllClasses(MySuperClass);
The approach I took worked around the build order problem by using the Context.onGenerate hook to build an array of matching classes at the end of compilation (rather than as each class is compiled) and add the array to “metadata”, which can still be modified in the “onGenerate” step. I then use this metadata in a separate runtime class to get the array of classes at runtime.

Unable to instantiate nested exception class due to constructor not found

I want to define a BarException inside of class Foo for namespacing reason. This is what my Foo class looks like:
package org.company.utils
class Foo {
class BarException extends RuntimeException { }
def raise() {
def r = new RuntimeException("BOOM")
throw new BarException(r)
}
}
This is how I would have liked it to work:
void testFoo() {
shouldFail(Foo.BarException) {
def foo = new Foo()
foo.raise()
}
}
But the test failed with:
1) testFoo(test.FooTestCase)java.lang.AssertionError: Closure
test.FooTestCase$_testFoo_closure1#3eb25e1a should have failed with an
exception of type org.company.utils.Foo$BarException, instead got
Exception groovy.lang.GroovyRuntimeException: Could not find matching
constructor for:
org.company.utils.Foo$BarException(org.company.utils.Foo,
java.lang.RuntimeException)
I have tried instantiating BarException using new BarException(this, r), and putting groovy.transform.InheritConstructors around BarException, but nothing works for the compiler.
What's the problem?
Try making BarException static and add #InheritConstructors.
import groovy.transform.InheritConstructors
class Foo {
#InheritConstructors
static class BarException extends Exception { }
def raise() {
Throwable r = new RuntimeException("BOOM")
throw new BarException(r)
}
}
From the groovy docs:
The usage of static inner classes is the best supported one. If you
absolutely need an inner class, you should make it a static one.

Groovy: #Immutable results in Setters

I'm having trouble with (or I'm confused about) using #Immutability.
Given this class:
#Immutable
class TestField {
String key
}
I would expect I could not set a key on an instance like:
class TestFieldSpec extends Specification {
def 'do stuff'() {
when:
def t = new TestField('a')
println t
t.key = 'b'
println t
then:
t
}
}
However, when I run the test, I do not see a ReadOnlyPropertyException:
Running TestFieldSpec
TestField(a)
TestField(b)
This has been the case for me using both the groovy-eclipse-compiler and gmaven-plus with Maven.
Groovy version 2.4.7.
If I run javap on the class file created by both, I see:
public final void setKey(java.lang.String);
When I do the same in GroovyConsole though, things work how I'd expect them to.
#groovy.transform.Immutable
class TestField {
String key
}
def f = new TestField('a')
f.key = 'b'
Produces:
Exception thrown
groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: key for class: TestField
at TestField.setProperty(ConsoleScript1)
at ConsoleScript1.run(ConsoleScript1:7)
In the Groovy AST Browser, at Phase Finalization, I do not see the setter.
Any insight would be greatly appreciated.
Thanks!

groovy immutable object with parent class

I have two immutable groovy classes that have a few shared values that I'm trying to abstract to a parent class. However when I create the following, the second test case always fails. Although everything compiles correctly and no error is thrown at runtime, when I assign the parent property int he constructor, it is never set, resulting in a null value. I havent found any documentation that forbids this, but I'm wondering is this even possible? I've tried a number of configuration of Annotations and class-types (e.g. removing abstract from the parent) but nothing seems to work short of just removing the #Immutable tag altogether.
abstract class TestParent {
String parentProperty1
}
#ToString(includeNames = true)
#Immutable
class TestChild extends TestParent {
String childProperty1
String childProperty2
}
class TestCase {
#Test
void TestOne() {
TestChild testChild = new TestChild(
childProperty1: "childOne",
childProperty2: "childTwo",
parentProperty1: "parentOne"
)
assert testChild
assert testChild.parentProperty1
}
}
Based on the code for the ImmutableASTTransformation, the Map-arg constructor added by the createConstructorMapCommon method does not include a call to super(args) in the method body.
which means that immutable classes are self contained by default
Now if you want to do it you need to use composition instead of inheritance and this is an example of how you can do it :
import groovy.transform.*
#TupleConstructor
class A {
String a
}
#Immutable(knownImmutableClasses=[A])
class B {
#Delegate A base
String b
}
def b = new B(base: new A("a"), b: "b")
assert b.a
i hope this will help :)

Groovy Compile time AST transformation: Assignment to a field

I'm currently trying to implement some Groovy compile time AST transformations, but I ran into trouble:
How do I specify an AST transformation for an assignment statement to a field? i.e. the AST transformation should transform the following code:
class MyClass {
#MyTransformation
String myField
public void init() {
}
}
into something like
class MyClass {
String myField
public void init() {
this.myField = "initialized!"
}
}
I tried it with this AST builder invocation:
def ast = new AstBuilder().buildFromSpec {
expression{
declaration {
variable "myField"
token "="
constant "initialized!"
}
}
}
But after inserting the resulting statement in the "init" method of the declaring class, it instead inserted a variable assignment, as in
java.lang.Object myField = "initialized!"
I looked through the examples incorporated in the Ast Builder TestCase, but they only cover field declaration in the class body, not assignments to fields. My own tries using fieldNode all resulted in compiler errors. I set the compile phase to INSTRUCTION_SELECTION; I think this should be fine.
How do I achieve this? A solution based on the AstBuilder#buildFromSpec method is preferred, but any help would be highly appreciated.
I usually recommand not to use the AST builder. It's good for prototyping, but you don't really control what it generates. In particular, here, it's not capable of handling the fact that the variable expression you create should reference the field node. AST Builder is very nice to learn about the AST, but shouldn't be used in production code IMHO.
Here is a self contained example that demonstrates how you can acheive what you want. The code inside #ASTTest would correspond to your transform code:
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.syntax.Token
import org.codehaus.groovy.syntax.Types
class MyClass {
String myField
#ASTTest(phase=SEMANTIC_ANALYSIS,value={
def classNode = node.declaringClass
def field = classNode.getDeclaredField('myField')
def assignment = new BinaryExpression(
new VariableExpression(field),
Token.newSymbol(Types.EQUAL, 0, 0),
new ConstantExpression('initialized!')
)
node.code.addStatement(new ExpressionStatement(assignment))
})
public void init() {
}
}
def c = new MyClass()
c.init()
println c.myField
Hope this helps!

Resources