Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KorTE: Accessing Map's declared properties #1413

Closed
olehs opened this issue Mar 14, 2023 · 4 comments · Fixed by #1433
Closed

KorTE: Accessing Map's declared properties #1413

olehs opened this issue Mar 14, 2023 · 4 comments · Fixed by #1433

Comments

@olehs
Copy link

olehs commented Mar 14, 2023

Is it possible to access Maps' declared properties from templates ?

Imagine this class as sample

class TemplateTimeStamp : LinkedHashMap<String, Any?>() {
    val time: String? by this.withDefault { null }
    val date: String? by this.withDefault { null }

    val dateTime: Date?
        get() = if (time != null && date != null) {
            SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT).parse("$date $time")
        } else null
}

How can I get dateTime value from template?

Ref. code:

suspend fun accessAny(instance: Any?, key: Any?, mapper: ObjectMapper2): Any? = when (instance) {
null -> null
is Dynamic2Gettable -> instance.dynamic2Get(key)
is Map<*, *> -> instance[key]
is Iterable<*> -> instance.toList()[toInt(key)]
else -> {
val keyStr = DynamicContext { key.toDynamicString() }
when {
mapper.hasProperty(instance, keyStr) -> {
//println("Access dynamic property : $keyStr")
mapper.get(instance, key)
}
mapper.hasMethod(instance, keyStr) -> {
//println("Access dynamic method : $keyStr")
mapper.invokeAsync(instance::class as KClass<Any>, instance as Any?, keyStr, listOf())
}
else -> {
//println("Access dynamic null : '$keyStr'")
null
}
}
}
}

@soywiz
Copy link
Member

soywiz commented Mar 14, 2023

Is not this line doing the trick?

@olehs
Copy link
Author

olehs commented Mar 14, 2023

Unfortunately, this line actually causes the problem.

It doesn't allow to access dateTime property, as it tries to find key 'dateTime' in the map.
But dateTime property can be accessed only with mapper.

Here is modified version that can clarify my point

suspend fun accessAny(instance: Any?, key: Any?, mapper: ObjectMapper2): Any? {
        val keyStr = DynamicContext { key.toDynamicString() }
        return when {
            instance == null -> null
            instance is Dynamic2Gettable -> instance.dynamic2Get(key)
            mapper.hasProperty(instance, keyStr) -> {
                //println("Access dynamic property : $keyStr")
                mapper.get(instance, key)
            }
            mapper.hasMethod(instance, keyStr) -> {
                //println("Access dynamic method : $keyStr")
                mapper.invokeAsync(instance::class as KClass<Any>, instance as Any?, keyStr, listOf())
            }
            instance is Map<*, *> -> instance[key]
            instance is Iterable<*> -> instance.toList()[Dynamic2.toInt(key)]
            else -> {
                //println("Access dynamic null : '$keyStr'")
                null
            }
        }
    }

More 'Kotlin' way would be to access instance['key'] as map fields and instance.key as object properties.
But I guess this would break compatibility with current implementation

@soywiz
Copy link
Member

soywiz commented Mar 15, 2023

But then methods available in the Map interface/implementation could be called instead.
This tries to mimic the behaviour of liquid template engine. And that's the usual behaviour: maps and objects act the same.

Could you consider doing something else like?:

class TemplateTimeStamp(val map = LinkedHashMap<String, Any?>()) {
    val time: String? by map.withDefault { null }
    val date: String? by map.withDefault { null }

@olehs
Copy link
Author

olehs commented Mar 15, 2023

I get your point, but in real implementation things are a little bit more complicated (include custom delegated properties, nested maps, participate in serialization etc.)

Also, approach you suggest will not allow to access Map's keys directly.

I think another solution could be moving accessAny processing to Mapper so this behavior could be overridden.
If you think this can't be done, please close the issue freely.

BTW, thank you for great library and support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants