Groovy #Immutable class with (im)mutable property - groovy

I'm trying to use Groovy #groovy.transform.Immutable to implement classes with properties of unsupported "immutable" types. In my case it is java.io.File
For example, having class like
#groovy.transform.Immutable class TwoFiles {
File file1,file2
}
gives me following compile error
Groovyc: #Immutable processor doesn't know how to handle field 'file1' of type 'java.io.File' while compiling class TwoFiles.
#Immutable classes only support properties with effectively immutable types including:
- Strings, primitive types, wrapper types, BigInteger and BigDecimal, enums
- other #Immutable classes and known immutables (java.awt.Color, java.net.URI)
- Cloneable classes, collections, maps and arrays, and other classes with special handling (java.util.Date)
Other restrictions apply, please see the groovydoc for #Immutable for further details
One option I found it to extend java.io.File to make it Cloneable but I'm not happy with this solution. Following code compiles and works, but having own subclass of java.io.File is not what I'd like.
#groovy.transform.Immutable class TwoCloneableFiles {
FileCloneable file1,file2
class FileCloneable extends File implements Cloneable{
FileCloneable(String s) {
super(s)
}
// ... and other constructors ...
}
}
So my question is: Is there any other option how to use java.io.File directly in such class?
Possibly to mark java.io.File as "known immutable" for the purpose of #groovy.transform.Immutable (same as it seems to be done for java.awt.Color, java.net.URI)?

Have you tried using knownImmutableClasses to specify File? Something like this should work:
#groovy.transform.Immutable(knownImmutableClasses = [File])
class TwoFiles {
File file1,file2
}
(With File, you could probably also get rougly the effect you want with the following:
#groovy.transform.Immutable
class TwoFiles {
String file1,file2
public File getFile1() {return new File(file1)}
public File getFile2() {return new File(file2)}
}
def f = new TwoFiles("/", "/Users")
assert f.file1.class == File
)

Related

How to reference subclass from a static superclass method in Groovy

A simplified version of what I'm trying to do in Groovy:
class Animal {
static def echo() {
println this.name // ie "class.name"
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Dog.echo()
Cat.echo()
// Output:
// => Animal
// => Animal
//
// What I want:
// => Dog
// => Cat
I think what I'm asking here is: when I call a static method on an object, and
the static method is defined in the object's superclass, is there a way to obtain
the actual type of the object?
A static method is not defined in the object context, but in the class context. You might get confused by the presence of this in the Groovy static method. However, it's only a syntactic sugar that eventually replaces this.name with Animal.class.name.
If you compile the Animal class from your example with a static compilation enabled, you will see that it compiles to the following Java equivalent (result after decompiling the .class file):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
public class Animal implements GroovyObject {
public Animal() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static Object echo() {
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
return null;
}
}
You can see that the following line in the echo method:
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
operates directly on the Animal class name. So from the echo method perspective, it doesn't matter how many classes extend it. As long as those classes invoke echo method defined in the Animal class, you will always see Animal printed as a result.
And there is even more than that. If you use the following compiler configuration script:
config.groovy
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
ast(groovy.transform.TypeChecked)
}
and then compile the script (let's call it script.groovy) using this configuration option with the following command:
groovyc --configscript=config.groovy script.groovy
then you will see something like this after decompiling the .class file:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import org.codehaus.groovy.runtime.InvokerHelper;
public class script extends groovy.lang.Script {
public script() {
}
public script(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(script.class, args);
}
public Object run() {
Animal.echo();
return Animal.echo();
}
}
You can see that even though you have invoked Dog.echo() and Cat.echo() in your Groovy script, the compiler replaced these calls with the double Animal.echo() invocation. It happened because calling this static method on any other subclass does not make any difference.
Possible solution: applying double dispatch
There is one way to get the expected output - override echo static method in Dog and Cat class. I can assume that your real method may do something more than the exemplary echo method you have shown above, so you might need to call the super echo method from a parent class. But... there are two problems: (1) you can't use super.echo() in the static context, and (2) it doesn't solve the problem, because parent method still operates in the Animal class context.'
To solve this kind of issue you might want to mimic a technique called double dispatch. In short - when we don't have information about the caller in the method that was called, let's allow the caller to pass this information with the method call. Consider the following example:
import groovy.transform.CompileStatic
#CompileStatic
class Animal {
// This is a replacement for the previous echo() method - this one knows the animal type from a parameter
protected static void echo(Class<? extends Animal> clazz) {
println clazz.name
}
static void echo() {
echo(Animal)
}
}
#CompileStatic
class Dog extends Animal {
static void echo() {
echo(Dog)
}
}
#CompileStatic
class Cat extends Animal {
static void echo() {
echo(Cat)
}
}
Animal.echo()
Dog.echo()
Cat.echo()
This may sound like a boilerplate solution - it requires implementing echo method in each subclass. However, it encapsulates the echo logic in the method that requires Class<? extends Animal> parameter, so we can let every subclass to introduce their concrete subtype. Of course, this is not a perfect solution. It requires implementing echo method in each subclass, but there is no other alternative way. Another problem is that it doesn't stop you from calling Dog.echo(Animal) which will cause the same effect as calling Animal.echo(). This double dispatch like approach is more like introducing a shorthand version of echo method which uses the common static echo method implementation for simplicity.
I don't know if this kind of approach solves your problem, but maybe it will help you find a final solution.

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 :)

Import inner enum in Groovy script

I have a Groovy class defined in Vehicles.groovy that contains some inner enums:
public class Vehicles {
public enum Land {
BICYCLE,
CAR,
TRAIN
}
public enum Water {
SAILBOAT,
MOTORBOAT
}
public enum Air {
JET,
HELICOPTER
}
}
I'd like to reference these enums in a script run.groovy in the same directory as Vehicles.groovy.
Fully qualifying the enum instance works.
import Vehicles
println Vehicles.Land.BICYCLE
or
import static Vehicles.Land
println Vehicles.Land.BICYCLE
or
import Vehicles.Land.*
println Vehicles.Land.BICYCLE
correctly print BICYCLE.
However, I'd like to reference the Land enum without fully qualifying it.
I basically tried every combination of static/non-static, aliased/non-aliased, and star/non-star imports.
import Vehicles.Land or import static Vehicles.Land.* (or import Vehicles.Land as Land) give unable to resolve class errors. This seems weird because they're what one would do in Java (correct me if I'm wrong.)
If I try
import static Vehicles.Land
println Land.BICYCLE
or
import static Vehicles.Land as Land
println Land.BICYCLE
or
import Vehicles.Land.*
println Land.BICYCLE
, I get the error
Caught: groovy.lang.MissingPropertyException: No such property: Land for class: run
groovy.lang.MissingPropertyException: No such property: Land for class: run
at run.run(run.groovy:2)
Similarly,
import Vehicles.Land.*
println BICYCLE
gives
Caught: groovy.lang.MissingPropertyException: No such property: BICYCLE for class: run
groovy.lang.MissingPropertyException: No such property: BICYCLE for class: run
at run.run(run.groovy:2)
Adding package declarations to both Vehicles.groovy and run.groovy doesn't seem to help, either.
So...
What support does Groovy have for importing inner classes? Why is it it different from Java?
How can I get Groovy to allow me to reference non-fully-qualified inner enums?
Note: I'm using Groovy 1.8.6 and Oracle JDK 1.8.0_45.
Groovy does support import nested classes, including enums. However to access them without full qualification, you'll need to import them in a non-static manner (unlike Java), or explicitly declare them static:
// Explicitly declare Water and Air as static to demonstrate
public class Vehicles {
public enum Land { BICYCLE, CAR, TRAIN }
public static enum Water { SAILBOAT, MOTORBOAT }
public static enum Air { JET, HELICOPTER }
}
// Non-static nested enum needs non-static import (unlike Java)
import Vehicles.Land
println Land.BICYCLE
// Explicitly static nested enum can be static imported
import static Vehicles.Water
println Water.SAILBOAT
// Explicitly static nested enum can also be non-static imported as well!
import Vehicles.Air
println Air.JET
Working example: https://groovyconsole.appspot.com/script/5089946750681088
Unlike Java where enums are implicitly static, it appears that enums in Groovy are not implicitly static, hence why static imports don't work. This is because enums in Groovy aren't actually the same as the ones in Java, they made enhancements. Unfortunately it seems they have forgotten to tell the compiler to also make them implicitly static (at least as of 2.4.4).
My suggestion is to explicitly declare them static (if you can) as it would be keeping with the Groovy notion that valid Java is valid Groovy.
Have you tried below?
import static Vehicles.Land.*
println BICYCLE
EDIT: is this what you are looking for?

Groovy AST - Adding annotations at compilation

I'm trying to dynamicly crate an annotation that will dynamicaly add an #XmlElement annotation to every field in a class using metaprogramming and AST. I'm having problems creating the annotations and applying them to the fields properly.
The code i have is formatted here: http://pastebin.com/60DTX5Ya
import javax.xml.bind.annotation.XmlElement
#GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class WebserviceAnnotationModifier implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
if (!astNodes) return
if (!astNodes[0] || !astNodes[1]) return
if (!(astNodes[0] instanceof AnnotationNode)) return
if (!(astNodes[1] instanceof ClassNode)) return
ClassNode node = (ClassNode)astNodes[1]
List fields = node.getFields()
fields.each {FieldNode field ->
field.addAnnotation(ClassHelper.make(new XmlElement.DEFAULT()));
}
}
}
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.TYPE])
#GroovyASTTransformationClass(classes =[WebserviceAnnotationModifier])
public #interface WebresourceAnnotation{}
#WebresourceAnnotation
class TestPerson{
String name;
String lastName;
int Age
}
Am i approaching this all wrong? The reason i do this is i have a domain that is still in the making and i'd like to just go in and apply the annotation to all fields. Couldn't find any examples of annotations added during compilation. Is this not possible?
Writing codes using Groovy AST Transformation alone does not work with the Grails reloading mechanism. Here's a proper way to implement AST transformation for a Grails app.
Your transformer class must extends AbstractGrailsArtefactTransformer.
Your transformer class must be annotated by #AstTransformer.
You class must be put under org.codehaus.groovy.grails.compiler or its sub-package. In my case I use org.codehaus.groovy.grails.compiler.zk and it's working fine.
Implement shouldInject() to match only classes you want, in this case domain classes.
Override performInjection() and write your codes there.
Pack your transformer and releated classes into a .jar file, or Grails compiler does not load it.

#Delegate class without default constructor

How can I create a delegate class in Groovy for a class which doesn't have a default constructor? I would like to decorate JUnit's ResultPrinter but am getting an error about the missing constructor.
I don't understand your issue. I just tried this with Java's Short — which also does not have a default constructor.
Everything worked as expected, except if you didn't initialize the delegated object, you get an NPE.
Is it possible you are using #Delegate incorrectly? Delegate doesn't decorate existing classes, it allows you to use an existing classes methods in your own class. It's like extend, but without the class inheritance.
Example code:
class Foo {
#Delegate Short num
String bar
String toString() { "$bar: $num" }
}
def f = new Foo(bar: 'bob', num: 34 as Short)
println f // OK
println f.doubleValue() // OK
f = new Foo()
println f.doubleValue() // NPE
(Alternatively, providing some useful information, such as the actual error and stacktrace, and example code, will get you more useful responses.)

Resources