I have groovy script that has a function with 2 arguments, one of which is a hashmap. I want to check the name of a hashmap variable contains a substring or not, inside a function. How do i do it?
def myfunc(String var, HashMap var2)
{
// need a routine to retrive the name of the variable var2
}
It's well explained here :
Getting the name of a method parameter
To access groovy script's method follow syntax :
metaClass.methods
Sounds like you want the --parameters (alias -pa) compiler argument.
Generates metadata for reflection on method parameter names on JDK 8 and above. Defaults to false.
Usage: groovy --parameters Person.groovy
Also available for groovyc, groovyConsole, groovysh and groovy ant tasks. Check out the PR some awesome person contributed...
e.g.
class HelloWorld {
static void speak(String firstName) {
println "Hello $firstName"
}
}
Student.methods.each {
if (it.name === 'speak') println it.parameters
}
// groovy HelloWorld.groovy => "[java.lang.String arg0]"
// groovy -pa HelloWorld.groovy => "[java.lang.String firstName]"
Related
I have code accepts a class as a parameter and prepares data to call either the constructor for that class of a companion object factory method if the factory method is present.
All works fine when calling the constructor, but I get the error
java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun nz.salect.objjson.JVMTest.StudentWithFactory.Companion.fromJson(kotlin.String, kotlin.Int): nz.salect.objjson.JVMTest.StudentWithFactory
when calling the factory method. The factory method in question:
data class StudentWithFactory(val name: String, val years: Int=0) {
companion object {
fun fromJson(name: String="", age: Int = 0):StudentWithFactory {
return StudentWithFactory(name, age)
}
}
}
has no required parameters, unless there is some hidden parameter. Any ideas?
In fact, I reverted removing the parameters completely from fromJson and directly calling the companion method using ::fromJson.callby(emptyMap()). Same error.
It is clear that companion methods need at least one additional parameter. Perhaps the class? Or the companion object?
How can I specify the needed parameter(s)?
The function building up the callBy() is supplied a class (or finds the class from a supplied class) and json names and values.
var funk:KFunction<*>?=null
val companionFuncs=cls.companionObject?.declaredMemberFunctions
if(companionFuncs?.size ?:0 >0){
companionFuncs?.forEach {
if(it.name == "fromJson") funk=it
}
}
val cons:KFunction<T> = if(funk != null)
funk as KFunction<T>
else
cls.primaryConstructor ?: throw IllegalArgumentException("no primary constructor ${cls.simpleName}")
val valuesMap = cons.parameters.filter{it.name in vals}
.associateBy(
{it},
{generateValue(it)}
)
val data = cons.callBy(valuesMap) //as T
return data
In addition to my short answer, a more technical explanation:
Yes, there actually is a hidden parameter and you can see it (for example), if you take a look at the decompiled (to Java) bytecode:
public final class StudentWithFactory {
// ...
public static final class Companion {
// ...
#NotNull
public static StudentWithFactory fromJson$default(StudentWithFactory.Companion var0, String var1, int var2, int var3, Object var4) {
// ...
return var0.fromJson(var1, var2);
}
// ...
}
}
The first parameter (var0) is actually an instance of the companion object. var1 (name) and var2 (age) are the parameters you declared. var3 is a bitmask for determining if explicit values have been passed or if the default ones should be used*. I honestly don't know what var4 is for. It is unused in the Java code. But the imported part is that you only need to worry about var0, var1 and var2 if you want to invoke the function.
So, in the end the non-static version of fromJson* is actually invoked on the instance of the companion object:
var0.fromJson(var1, var2)
*left code out for simplicity
You can use the parameters property to determine how much parameters you have to pass to the function/constructor.
If you call
val paramsConstr = StudentWithFactory::class.primaryConstructor?.parameters
paramsConstr will be of size two as expected, but if you call
val paramsFunc = ::fromJson.parameters
paramsFunc will be of size three. The first element corresponds to the instance of the companion object. So, thats the list of parameters you need to provide.
You can invoke the fromJson like this:
// not using any default parameters
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[1] to "Hello",
paramsFunc[2] to 30
))
// using only the default parameter for "name"
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[2] to 30
))
I have the following javascript method:
myFunc = function(callback) { callback.call(this, "hello", "world"); }
and I´m passing a java object that implements the 'call' method. In the java call method I get the two parameters "hello" and "world", but not 'this' (of course). Is there a way to access 'this' from java?
I´m interfacing java with d3.js and d3 has lots of callbacks in this way and 'this' is where d3 stores a selection.
Thanks
I´m not actually coding in Java but JRuby. In order to make a Java example
I´ll have to simplify my code bellow. Maybe this can help some. If not,
I´ll try to do a Java example.
# Function f1 will call the callback methods cb1 and cb2 with 'this' variable
# This is just a notation for creating javascript function. It calls
# #browser.executeJavaScriptAndReturnValue(scrpt), whith the function
# body (everything between EOT) modified to make a valid javascript script.
# f1 is a function with 2 arguments cb1, and cb2 which should be the
# callback functions
f1 = B.function(<<-EOT)
(cb1, cb2) {
cb1.call(this, "banana", "milk");
cb2.call(this, "arroz", "feijao");
}
EOT
# Proc is a closure. It receives two arguments |food1, food2|. This will
# become a java object per JRuby´s magic
proc = Proc.new { |food1, food2| puts "I hate #{food1} and #{food2}" }
# now call method f1 passing proc as the first argument and the block as
# the second argument. So cb1 = proc and cb2 = <the block bellow>. Method
# 'send' grabs the given arguments, converts them to java objects and then
# calls jxBrowser 'invoke' method with the given arguments.
f1.send(proc) { |food1, food2| puts "eu gosto de #{food1} e #{food2}" }
The result of executing this code is:
I hate banana and milk
eu gosto de arroz e feijao
As can be seen, the 'this' variable is just gone... I would like to be able to
capture the 'this' variable somehow in order to be able to use the context in the blocks. I´ve managed to make a workaround that allows capturing the 'this' variable, but it requires wrapping the block in another javascript function.
The whole idea of this code is to allow a JRuby developer to write Ruby code and get this code executed in jxBrowser without needing to use any javascript. Examples of this can already be seen by downloading mdarray-sol GEM, or going to https://github.com/rbotafogo/mdarray-sol. There you can see multiple examples of using d3.js with JRuby.
Please make sure that you follow the instruction at https://jxbrowser.support.teamdev.com/support/solutions/articles/9000013062-calling-java-from-javascript and inject your Java object with the call() method correctly:
Java code:
browser.addScriptContextListener(new ScriptContextAdapter() {
#Override
public void onScriptContextCreated(ScriptContextEvent event) {
Browser browser = event.getBrowser();
JSValue window = browser.executeJavaScriptAndReturnValue("window");
window.asObject().setProperty("java", new JavaObject());
}
});
...
public static class JavaObject {
public void call(JSValue window, String message) {
System.out.println(message);
}
}
JavaScript code:
window.java.call(window, 'Hello Java!');
The Swift Programming Language guide, page Language Reference -> Expressions, section Postfix Expressions -> Initializer Expression talks about the syntax
<expression>.init(<initializer arguments>)
When would you use this (apart from self.init() and super.init() which are already covered in other parts of the grammar)? It seems to me that in any case where the above expression is valid (and the expression is not self or super), you can also do:
<expression>(<initializer arguments>)
While the syntax template looks like this:
<expression>.init(<initializer arguments>)
the grammar just defines an initializer-expression as:
initializer-expression → postfix-expression . init
Note that the invocation arguments are not included. This matches constructs like A.init, and is necessary because init is a keyword, so A.init will not match explicit-member-expression (which requires an identifier rather than a keyword as the member name).
Initializer expressions are a part of the grammar because you can invoke an initializer that belongs to class that is not the direct superclass of the class you are writing (C invokes A.init in this example):
class A {
var property: String
init() {
property = "hello"
}
}
class B: A {
init() {
super.init()
property = "goodbye"
}
}
class C: B {
init() {
super.init()
A.init()
}
}
C().property // "hello"
I don't think there are many use cases for this, but both the grammar and the compiler allow it and this example compiles and runs correctly.
You can also use an initializer expression in the global scope like you would a normal initializer. The following two lines are equivalent:
String(10)
String.init(10)
I have a unit test that I am writing and have run into an annoying problem... Let's say I have the following function I am testing:
public function functionToTest(array &$data, parameter2)
{
// perform some action on the array that is being passed in by reference
}
Now, when I attempt to call this function in my unit test, I would do something like this:
public function testMyFunction()
{
$data = array('key1' => 'val1');
$mockOfClass = $this->getMockBuilder('ClassName')
->disableOriginalConstructor()
->setMethods(array('method1', 'method2')) // My function to test is NOT in this list
->getMock();
$this->mockOfClass->functionToTest($data, true);
// Perform assertions here
}
However, I receive the following error message:
Parameter 1 to ClassName::addNewFriendsToProfile() expected to be a reference, value given
This seemed very strange to me. First of all, I'm just passing an array by reference, so it it shouldn't have a problem with this. Secondly, Why parameter 1? Doesn't it mean parameter 0? Then, I tried changing the call to the following:
$this->mockOfClass->functionToTest(&$data, true);
After making this change, it works fine. Unfortunately, it also produces the following warning:
Call-time pass-by-reference has been deprecated in /PathToFile on line xxx
I do not encounter this error when running the actual code. It only throws this error in the unit test. Also, I need to use the mock as there are methods in the class I am mocking; So I can't simply create a new instance of the class and call the method that is being tested. Is there any way I can get around this?
It turns out that PHPUnit clones each of the parameters that are being passed in (Thanks Tim Lytle for pointing me to this source: Pass by reference in a callback when mocking in PHPUnit). This is what causes the error if the array is passed in without a reference at call-time in the unit test. Luckily, the solution is simple. Instead of passing the array by reference, I pass in the array by value and return the array.
before:
public function someFunction(array &$myArray)
{
$myArray[] = 'new val';
}
after:
public function someFunction(array $myArray)
{
$myArray[] = 'new val';
return $myArray;
}
What does # means before a field name in Groovy? For some classes I am able to access private fields that are not directly accessible, let's take ComposedClosure for example:
public class Person {
private String name
}
def u = new Person(name:"Ron")
println u.#name //Ron
println u.name //Ron
a = {2} >> {3}
println a.#first //first closure object
println a.first //runtime error
It allows you to override groovy's use of property accessors. If you write:
println u.name
groovy will invoke the automatically generated getter Person.getName(). If you write:
println u.#name
it will go directly to the field like it would in Java. In the case of the closure, it seems to have a first field but not a corresponding getFirst accessor.
In the groovy manual, it's documented as the direct field access operator.
It means you're accessing a field directly, rather than going through a getter.
See the Groovy operator docs, although there isn't much more to say. Other than probably avoid it.
The reason it fails for a ComposedClosure is because there's no getter for first (or second).