Kotlin KCallable illegalArgumentException - kotlin

I have the following Kotlin function:
fun invokeSync(typedArguments : List<Any?>): Any?{
var returnedValue : Any?
try {
returnedValue = callable.call(this, typedArguments);
} catch (e:Exception) {
logInvocationError(e, typedArguments);
throw IllegalArgumentException(e);
}
}
It doesn't matter how much arguments are in this list, I will always get an IllegalArgumentException saying "Callable expects 3 arguments, but 1 were provided".
The function is a simple isGreater-function with 2 arguments of type Int.
I have checked the list of arguments and there are 2 arguments of type Int in there.
Here the function in context:
open class TypedJavaScriptFunction(name: String) : SelfRegisteringJavascriptFunction(MessageFormat.format(JS_NAME_CONVENTION, name)) {
val callable = getCallable(this::class)
override fun function(arguments: Array<Any?>): Any? {
try {
val typedArguments = getTypedArguments(arguments)
val annotations = callable.annotations
for (a in annotations) {
if (a is BrowserFunction) {
if (a.value == Policy.ASYNC) {
invokeAsync(typedArguments);
return null
} else {
return invokeSync(typedArguments)
}
}
}
} catch (e: IllegalArgumentException) {
// this Exception is only for signaling the error; it has already
// been logged before
JavaScriptAPI.showError(browser, "Internal Error (" + callable.name + ")");
}
return null
}
fun getTypedArguments(arguments: Array<Any?>): List<Any?> {
var typedArguments = mutableListOf<Any?>()
val argTypes = callable.valueParameters
if (arguments.size != argTypes.size) {
LOG.error(getName()
+ ": given arguments don't match signature. Given: "
+ arguments.size + ", expected: " + argTypes.size);
throw IllegalArgumentException()
}
for (i in 0 until arguments.size) {
typedArguments.add(TypeRefinery.refine(arguments[i], argTypes[i].type.classifier as KClass<Any>))
}
return typedArguments
}
// ...
fun invokeSync(typedArguments: List<Any?>): Any? {
var returnedValue: Any?
try {
returnedValue = callable.call(this, typedArguments);
} catch (e: Exception) {
logInvocationError(e, typedArguments);
throw IllegalArgumentException(e);
}
// ...
}
}
Did anyone can help me and tell me whats wrong or can give me a hint?

Since call takes a vararg you need to use the spread operator * and toTypedArray() to pass in the List like that:
returnedValue = callable.call(this, *typedArguments.toTypedArray());
The first argument is the instance you are calling the function on and the other two parameters come from the spreaded List, under the condition that List has exactly two elements.

Related

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

Completion block with multiple params in Kotlin

In my application I use Retrofit to make a network call and I pass a completion block to the wrapping function:
fun issueToken(phoneNumber: String, otp: String,completion:(token: Token?, errorString: String) -> Unit) { //TODO: return something??
retrofitInstance.issueToken(LOGIN_GRANT_TYPE, LOGIN_CLIENT_ID,LOGIN_CLIENT_SECRET,phoneNumber, otp).enqueue(object : retrofit2.Callback<IssueTokenResponse> {
override fun onFailure(call: retrofit2.Call<IssueTokenResponse>, t: Throwable) {
completion(null, "NetworkManger: issueToken failed")
}
override fun onResponse(call: retrofit2.Call<IssueTokenResponse>, response: retrofit2.Response<IssueTokenResponse>) {
if (response.code() == 200) {
AccountManager.token.mAccessToken = response.body()?.access_token
AccountManager.token.mRefreshToken = response.body()?.refresh_token
AccountManager.token.mExpirationDate =
response.body()?.expires_in?.plus((System.currentTimeMillis() / 1000L));
completion(AccountManager.token, "")
} else {
//TODO
completion(null, "NetworkManager: issueToken response code = " + response.code() + " message = " + response.message() )
}
}
})
}
Now, I'm not sure how to call the issueToken method with the completion block, as I am new to Kotlin. I tried something like that:
NetworkManager.issueToken(view.phoneNumber.text.toString(),view.otp_edit_text_id.text.toString()) token, errorString -> { ... }
But I get errors that says:
No value passed for parameter 'completion'
I also have errors on this part:
token, errorString ->
What is the correct way to call the function with the completion block?
I suggest you to read kotlin lambda conventions first.
You should call like that:
issueToken(view.phoneNumber.text.toString(),view.otp_edit_text_id.text.toString()) { token: Token?, errorString: String ->
}
You may also use like that:
val completion = { token: Token?, errorString: String ->
// define whatever you need
}
issueToken(view.phoneNumber.text.toString(),view.otp_edit_text_id.text.toString(), completion)

Strings as objects kotlin

Actually this is an easy task, but I don't know why I still get errors like this
Failed test #4 of 8. Wrong answer
this is my code
fun main() {
val input = readLine()!!
if (input.isEmpty()){
println(input)
} else if (input.first().equals('i')) {
println(input.drop(1).toInt() + 1)
} else if (input.first().equals('s')) {
println(input.drop(1).reversed())
}}
I downloaded the failed test and the result is
ft
you can check the task in this link
Here is the correct answer
fun main() {
val input = readLine()!!
// write code here
if (input.isEmpty()){
println(input)
} else if (input.first().equals('i')) {
println(input.drop(1).toInt() + 1)
} else if (input.first().equals('s')) {
println(input.drop(1).reversed())
} else {
println(input)
}
}

Kotlin general setter function

I am new to kotlin. I wonder if this is possible
I wish to create a function that will change the value of the properties of the object and return the object itself. The main benefit is that I can chain this setter.
class Person {
var name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
try {
// the line below caused error
this[propName] = value
} catch(e:Exception){
println(e.printStackTrace())
}
}
}
}
//usage
var person = Person(null,null)
person
.setter(name, "Baby")
.setter(age, 20)
But I get error "unknown references"
This question is marked as duplicate, however the possible duplicate question specifically want to change the property of "name", but I wish to change anyProperty that is pass from the function to object. Can't seem to connect the dot between two questions. #Moira Kindly provide answer that explain it. thankyou
Why not just simplify your answer to
fun setter(propName: String, value: Any): Person {
val property = this::class.memberProperties.find { it.name == propName }
when (property) {
is KMutableProperty<*> ->
property.setter.call(this, value)
null ->
// no such property
else ->
// immutable property
}
}
Java reflection isn't needed, its only effect is to stop non-trivial properties from being supported.
Also, if you call it operator fun set instead of fun setter, the
this[propName] = value
syntax can be used to call it.
After googling around, I think I can provide an answer, but relying on java instead of kotlin purely. It will be great if someone can provide a better answer in kotlin.
class Person(
var name: String,
val age: Int
){
fun setter(propName: String, value: Any): Person{
var isFieldExistAndNotFinal = false
try{
val field = this.javaClass.getDeclaredField(propName)
val isFieldFinal = (field.getModifiers() and java.lang.reflect.Modifier.FINAL == java.lang.reflect.Modifier.FINAL)
if(!isFieldFinal) {
// not final
isFieldExistAndNotFinal = true
}
// final variable cannot be changed
else throw ( Exception("field '$propName' is constant, in ${this.toString()}"))
} catch (e: Exception) {
// object does not have property
println("$e in ${this.toString()}")
}
if(isFieldExistAndNotFinal){
val property = this::class.memberProperties.find { it.name == propName }
if (property is KMutableProperty<*>) {
property.setter.call(this, value)
}
}
return this;
}
}
usage like this
person
.setter(propName = "age", value = 30.00)
.setter(propName = "asdf", value = "asdf")
.setter(propName = "name", value = "A Vidy")
You have error because when you do this[propName] = value you are trying to use this as a list, but it is not a list, it is a Person and it doesn't overload the [] operator.
What you can do is to add a check for the property that is setted:
class Person {
privavar name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
if (propName == "name" && value is String?) {
it.name = value as String?
} else if (propName == "age" && value is Int?) {
it.age = value as Int?
} else {
// handle unknown property or value has incorrect type
}
}
}
}
Another more dynamic solution without reflection:
class Person {
private var fields: Map<String, Any?> = HashMap()
fun setter(propName:String, value:Any): Person{
return this.apply {
it.fields[propName] = value;
}
}
fun getName() = fields["name"]
}
If you want to get rid of the getters as well then you need to use reflection.

Mutate string from an extension

I was trying to port a Swift script to Kotlin but it's not working as expected, what the code does is to consume a string while the condition is true (need it for a parser). In Swift it works as expected but in Kotlin it doesn't (I just started with Kotlin a month ago so maybe I'm missing something).
Swift
extension String {
#discardableResult public mutating func consumeWhile(test: (String) -> Bool) -> String {
var chars = [Character](self.characters)
var result = ""
while chars.count > 0 && test(String(chars[0])) {
result.append(chars.remove(at: 0))
}
self = String(chars)
return result
}
}
Kotlin
fun String.consumeWhile(test: (String) -> Boolean): String {
if (isEmpty()) return ""
val chars = toCharArray().toMutableList()
var result = ""
var i = -1
while (chars.isNotEmpty() && test(chars.first().toString())) {
result += chars.removeAt(0)
++i
}
removeRange(0..i)
return result
}
So the basic usage will look like
val myString = "--Test" // IntelliJ suggests change var to val
val consumedString = myString.consumeWhile{ it != "-" }
println("result: $myString consumedString: $consumedString")
// expected: "result: Test consumedString: --"
// but got: "result: --Test consumedString: --"
Edit: Thanks for all the answers, don't know if will be possible to do like I want because as mentioned string are immutable in Kotlin/Java (just using the same string).
I forgot to mention that I need the consumed string, basically b/c I'm doing a parser so I need to store the consumed chars and the mutated string. I will leave open this question but I ended up creating a class that implements only a few String class methods.
class Line(var string: String) {
val length: Int
get() = string.length
fun consumeWhile(test: (String) -> Boolean): String {
if (string.isEmpty()) return ""
val chars = string.toCharArray().toMutableList()
var result = ""
while (chars.isNotEmpty() && test(chars.first().toString())) {
result += chars.removeAt(0)
}
string = chars.joinToString("")
return result
}
fun isNullOrEmpty(): Boolean {
return string.isNullOrEmpty()
}
fun isNotEmpty(): Boolean {
return string.isNotEmpty()
}
private fun removeRange(range: IntRange) {
string = string.removeRange(range)
}
operator fun get(i: Int): Char {
return string[i]
}
}
Usage example
val line = Line(string)
if (line.isNotEmpty() && line[0].toString() == "(") {
line.consumeWhile { it == "(" }
while (line.isNotEmpty() && line[0].toString() != ")") {
line.consumeWhile { it == " " }
val key = line.consumeWhile { it != "=" }
line.consumeWhile { it == "\"" || it == "=" }
val value = line.consumeWhile { it != "\"" }
line.consumeWhile { it == "\"" }
attributes[key] = value
}
line.consumeWhile { it == ")" }
}
String is immutable in Kotlin & Java, so you can't modify its state anyway.
You should avoiding to makes the wheels repeatedly, there is an existing function String#dropWhile(Char) in Kotlin. one thing you need to do is invert the condition, for example:
val result = "--Test".dropWhile { it == '-' }
// ^--- "Test"
In both Java and Kotlin String is immutable and you cannot change it after it has been created.
In swift this presumably can be turned off through the mutating modifier. However in Kotlin removeRange(0..i) creates a new String object which you then discard.
To have it behave as you want you will need to either:
Create a wrapper object that contains a string that can be replaced.
Return both the split string and the rest as a Pair, you can then use the destructuring operators to assign it as [_, myString] = myString.consumeWhile {}
Kotlin Strings are immutable and cannot be modified in place. Instead you can create a new String and return it
fun String.consumeWhile(test: (String) -> Boolean): String {
if (isEmpty()) return ""
val chars = toCharArray().toMutableList()
while (chars.isNotEmpty() && test(chars.first().toString())) {
chars.removeAt(0)
// Do something with the char
}
return chars.joinToString(separator = "")
}
Additionally, unless I am misunderstanding, your test condition should be it == "-" to get the result you want:
val myString = "--Test"
val newString = myString.consumeWhile{ it == "-" }
println("result: $newString")
You use
myString.consumeWhile{ it != "-" }
which stops consuming as soon as it meets the first "-", and thus it's nothing more to do.
The code works as it should, if you use
myString.consumeWhile{ it == "-" }
you will get the expected output as is correct.
I ended up creating a class that implements only a few String class methods.
class Line(var string: String) {
val length: Int
get() = string.length
fun consumeWhile(test: (String) -> Boolean): String {
if (string.isEmpty()) return ""
val chars = string.toCharArray().toMutableList()
var result = ""
while (chars.isNotEmpty() && test(chars.first().toString())) {
result += chars.removeAt(0)
}
string = chars.joinToString("")
return result
}
fun isNullOrEmpty(): Boolean {
return string.isNullOrEmpty()
}
fun isNotEmpty(): Boolean {
return string.isNotEmpty()
}
private fun removeRange(range: IntRange) {
string = string.removeRange(range)
}
operator fun get(i: Int): Char {
return string[i]
}
}
Usage example
val line = Line(string)
if (line.isNotEmpty() && line[0].toString() == "(") {
line.consumeWhile { it == "(" }
while (line.isNotEmpty() && line[0].toString() != ")") {
line.consumeWhile { it == " " }
val key = line.consumeWhile { it != "=" }
line.consumeWhile { it == "\"" || it == "=" }
val value = line.consumeWhile { it != "\"" }
line.consumeWhile { it == "\"" }
attributes[key] = value
}
line.consumeWhile { it == ")" }
}
Obs: for now will mark as answered, till a better solution comes out