GroovyClassLoader doesn't work properly with cyrillic symbols - groovy

I created a simple interface and groovy script that implements this interface.
class groovyClass implements MySimpleInterface {
#Override
String description (){
return "Описание";
}
}
But when I call this function from groovy class loader I get just a couple of unknown symbols (like this :Широкая СЌР) instead of my cyrillic one.
I'm using Intellij Idea
Also, when I'm writing it directly new Label ("Описание") it works just fine, but when I'm requesting it from groovy class loader it doesn't work

Related

Groovy star import and usage of "partial" packages

To my surprise I have learned today that the following works just fine in Groovy:
import java.util.concurrent.*
def atomicBool = new atomic.AtomicBoolean(true)
i.e. after the star import, I can use a 'partial' package to refer to java.util.concurrent.atomic.AtomicBoolean.
Obviously, the same is not valid in Java:
import java.util.concurrent.*;
public class Sample {
public static void main(String[] args) {
// compiler error: 'atomic' cannot be resolved to a type
new atomic.AtomicBoolean(true);
}
}
So it seems that Groovy's idea of a package is similar to C++ (or C#) namespaces in this regard.
Question to the Groovy experts:
Is this by design or is it a (potentially unintended) side effect of the way the interpreter treats star imports?
If it is by design, can you point me to a section in the documentation or language specification where this behavior is documented? (There is no mention of this in the documentation on Star Import and neither in the language spec as far as I can tell or at least I couldn't find anything.)
Based on Groovy source code, this behavior seems to be intended. Before we dig deeper into Groovy internals you have to be aware of one thing - Groovy compiles to a bytecode that can be represented by a valid Java code. It means that Groovy code like the one form your example actually compiles to something like this (without compile static and type checked transformations):
import groovy.lang.Binding;
import groovy.lang.Script;
import java.util.concurrent.atomic.AtomicBoolean;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
AtomicBoolean atomicBool = (AtomicBoolean)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(AtomicBoolean.class, true), AtomicBoolean.class);
return var1[2].callCurrent(this, atomicBool);
}
}
As you can see this Java class uses full java.util.concurrent.atomic.AtomicBoolean import and this is actually what Groovy transforms your input source code to.
How does it happen?
As you may know Groovy builds Abstract Syntax Tree (AST) from the input source file and it iterates over all nodes (like expressions, variable definitions, method calls etc.) and applies transformations. Groovy uses class called ResolverVisitor that is designed to resolve types. When Groovy compiles your code it finds ConstructorCallExpression:
new atomic.AtomicBoolean(true)
It sees that the expected type of the object you are trying to create is atomic.AtomicBoolean, so ResolverVisitor starts resolving the type by calling resolveOrFail(type, cce); at line 1131.
It tries several resolving strategies that fail until it reaches method resolveFromModule at line 695. What happens here is it iterates over all star imports (single java.util.concurrent.* in your case), then it concatenates star import with the type name and it checks if qualified name created from this concatenation is a valid type class. Luckily it is in your case:
When type gets resolved, Groovy replaces initial type with this resolved valid type name in the abstract syntax tree. After this operation your input code looks more like this:
import java.util.concurrent.*
java.util.concurrent.atomic.AtomicBoolean atomicBool = new java.util.concurrent.atomic.AtomicBoolean(true)
This is what compiler gets eventually. Of course fully qualified name gets replaced with the import (this is what Java compiler does with qualified names).
Was this "feature" introduced by designed?
I can't tell you that. However we can read from the source code, that this happens on purpose and type resolving like this is implemented with intention.
Why it is not documented?
I guess no one actually recommend using imports that way. Groovy is very powerful and you are able to do many things in a many different ways, but it doesn't mean you should do so. Star imports are pretty controversial, because using star imports over explicit imports makes your code more error-prone due to possible class import conflicts. But if you want to know exact answer to this kind of questions you would have to ask Groovy language designers and core developers - they may give you straight answer without any doubts.

Forbidden characters in Groovy script filenames?

I have written a tiny Groovy script (just a few simple instructions) and named it a-b.groovy.
My colleague has trouble running it and said:
I believe '-' is illegal in Groovy scripts filenames
Question: What is the complete list of characters that are illegal in Groovy filenames?
(Note: Unlike Naming convention for groovy script files, my question is not about conventions)
The most accurate answer is to use the same standards for naming Groovy scripts as we use for naming Java classes. Groovy, similarly to Java supports Unicode characters encoding and you can most probably use any Unicode character as a script name (it doesn't mean you should do it).
Every Groovy script gets compiled to a Java class that extends groovy.lang.Script class. Its name is taken from a Groovy script file name. Take a look at this example:
someSimpleGroovyScript.groovy
println "test"
When we compile it with groovyc someSimpleGroovyScript.groovy we will see a class file someSimpleGroovyScript.class. Now if we take a look what does this class look like:
javap someSimpleGroovyScript
we will get something like this:
Compiled from "someSimpleGroovyScript.groovy"
public class someSimpleGroovyScript extends groovy.lang.Script {
public static transient boolean __$stMC;
public someSimpleGroovyScript();
public someSimpleGroovyScript(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
Corner cases for Groovy script names
Even though Java class naming standards apply to Groovy script names, there are some corner cases that Groovy supports.
Using - character in the script name
You can name your script as a-b.groovy and run as a Groovy script:
groovy a-b.groovy
You can even compile it with groovyc to get following Java class:
Compiled from "a-b.groovy"
public class a-b extends groovy.lang.Script {
public static transient boolean __$stMC;
public a-b();
public a-b(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
Even though this class name incorrect for Java compiler, you can still run it with Java (you need to add groovy-all to the classpath):
java -classpath ".:./groovy-all-2.4.12.jar" a-b
Using $ in the script name
In Groovy you can even call your script as 123$.groovy and you can simply run it as:
groovy 123\$.groovy
Groovy compiler however will replace $ with underscore when compiling this script to Java class:
javap 123_
Output:
Compiled from "123$.groovy"
public class 123_ extends groovy.lang.Script {
public static transient boolean __$stMC;
public 123_();
public 123_(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
You can still run such compiled script with Java:
java -classpath ".:./groovy-all-2.4.12.jar" 123_
Using space in the script name
You can also use a space in your script name, e.g. this is script.groovy can be executed as:
groovy this\ is\ script.groovy
Groovy compiler however will replace every space with a underscore:
Compiled from "this is script.groovy"
public class this_is_script extends groovy.lang.Script {
public static transient boolean __$stMC;
public this_is_script();
public this_is_script(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
You can still run such compiled script with Java:
java -classpath ".:./groovy-all-2.4.12.jar" this_is_script
Using class.groovy as a script name
It goes even further. You can create a script called class.groovy that will compile to a following Java class:
Compiled from "class.groovy"
public class class extends groovy.lang.Script {
public static transient boolean __$stMC;
public class();
public class(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
protected groovy.lang.MetaClass $getStaticMetaClass();
}
javac compiler would never allow you compiling a class with such name, however java has no problems with running such bytecode:
java -classpath ".:./groovy-all-2.4.12.jar" class
As the file name of the script is used to generate the corresponding class name, it would be restricted to valid java class names
Valid characters in a Java class name
You can have almost any character, including most Unicode characters!
The exact definition is in the Java Language Specification under
section 3.8: Identifiers.
An identifier is an unlimited-length sequence of Java letters and Java
digits, the first of which must be a Java letter. ...
Letters and digits may be drawn from the entire Unicode character set,
... This allows programmers to use identifiers in their programs that
are written in their native languages.
An identifier cannot have the same spelling (Unicode character
sequence) as a keyword (§3.9), boolean literal (§3.10.3), or the null
literal (§3.10.7), or a compile-time error occurs.
I encountered a weird issue with a script containing a dash in the filename and with a function having a Closure as parameter
Let's consider this exemple, test-script.groovy :
#!/usr/bin/env groovy
void foo(String msg, Closure clo)
{
println msg
clo()
}
foo 'bar', { -> println 'closure-bar'}
On my dev env with Groovy 2.4.16 it works like a charm, it prints :
bar
closure-bar
On the prod env with Groovy 2.4.5, I get this error :
Caught: java.lang.ClassFormatError: Illegal class name "test-script$foo" in class file test-script$foo
java.lang.ClassFormatError: Illegal class name "test-script$foo" in class file test-script$foo
at test-script.run(test-script.groovy:9)
If I rename test-script.groovy to testScript.groovy, it works for both version 2.4.16 and 2.4.5
I tracked down the "bug fix" to Groovy 2.4.15. Prior to this version, it failed.
The changelog for this version does not indicate any relevant fix so maybe it has been enclosed in another bug fix.
Anyway, I've been stuck a few hours on this case so if it can help someone, good thing !

How to access external class within a class in groovy

I'm new to groovy...
I've made a class. Within this class, I'd like to call an external method : LOG.error, but somehow, groovy is complaining about the method not being part of the class... how should I call that external method ?
class GAPI{
private myvar
public getResult(){
this.myvar="blabla"
LOG.error("test")
}
}
Groovy provides #Slf4j annotation that can add log field to your class, e.g.
#Slf4j
class GAPI{
private myvar
public getResult(){
this.myvar="blabla"
log.error("test")
}
}
Alternatively you can use #Log annotation that adds log field that uses java.util.logging.Logger instead of one provided with Slf4j. However in this case you have to be aware that java.util.logging.Logger uses different API, so there is no log.error() method.
In your example Groovy throws MissingPropertyException, because LOG is not defined in your class. If there is a class LOG with static method error you will have to import this class. But most probably you should just create LOG field (with annotation or manually) and call it to be most explicit (otherwise your code gets unreadable).

Adding methods to class and use them in other groovy code

I'm very new to Groovy. I have a class where I'm adding methods using metaClass. Here is the code I have for Parser.groovy:
PrivateClass.metaClass.convertDDTToMap { obj,fileLocation ->
}
where PrivateClass is a class coming from a jar. Now in other file named Hack.groovy I have the following code:
class Hack extends PrivateClass
{
//.. code
convertDDTToMap(param,param)
}
when I run Hack.groovy, I get the exception that the method convertDDTToMap is not there.
However Parser.groovy is in the same classpath and it gets compiled. But its not adding the method.
Where I'm making the mistake?
Parser.groovy being compiled only is doing nothing, the code there needs to be called. For example using new Parser().run()

Is there any way to use groovy and java code for the same class?

I mainly program in groovy, but occasionally it's too slow. So I write a separate .java class, and put the code that needs to run faster in a java method and call that java method from my groovy code.
This causes me to end up with two separate files and two separate classes. Is there any way I could embed a java method right into the groovy file, maybe marking it with an annotation to indicate that it is java?
This is the idea behind groovy++. Marking a class or method with the #Typed annotation will cause it to use static typing instead of dynamic typing, while still retaining a lot of goodness of groovy.
While not exactly java, typed groovy++ methods generally perform about the same as java would.
More information on groovy++ is available at: https://code.google.com/p/groovypptest/wiki/Welcome
You don't need to do anything special.
Just write the Java class behind the groovy class. 99% of all Java source is valid groovy source as well.
class GroovyClass {
def a;
def doSomething(x,y) { return x*y; }
}
class JavaClass {
SomeType someVar;
public JavaClass() { /* ... */ } // some contructor
public void doit(String a, int b) {} // full typed method, that is java
}
Groovy++ is somethign completely different.
The JavaClass needs to have everything fully typed to be "Java" however your problem can be solved far easyer if you just use types in the relevant groovy methods.
class AnotherGroovyClass {
// typed
public Result someMethod(SomeArg arg1, SomeOtherArg arg2) {
}
def someVariable; // untyped
}
If you think the lack of speed comes from the dynamic nature of groovy then just use full types at the relevant points.

Resources