Is it possible to lazily initialize a property and assert it? - lazy-evaluation

How can I lazy init a field and execute an assert on it?
I tried
val table: Array<FormatInfo> by lazy {
val t = arrayOf(...)
assert(table.size == FORMAT_COUNT, { System.err.println("GLI error: format descriptor list doesn't match number of supported formats") })
t
}
But it goes in overflow:
java.lang.StackOverflowError
at gli.GliKt$table$2.invoke(gli.kt:451)
at gli.GliKt$table$2.invoke(gli.kt)
at kotlin.SynchronizedLazyImpl.getValue(Lazy.kt:131)
at gli.GliKt.getTable(gli.kt)
at gli.GliKt$table$2.invoke(gli.kt:693)
at gli.GliKt$table$2.invoke(gli.kt)
at kotlin.SynchronizedLazyImpl.getValue(Lazy.kt:131)
at gli.GliKt.getTable(gli.kt)
at gli.GliKt$table$2.invoke(gli.kt:693)...
How can I solve?

The stack trace looks like you are calling the property from inside the lazy {} lambda, which obviously makes it crush.
This should work:
val table: Array<FormatInfo> by lazy {
val t = arrayOf(...)
myAssert(t)
t
}

Related

Spring cache for specific values #Cacheable annotation

I want to cache a result of a method only when the attribute of the result contains specific values. For example
Class APIOutput(code: Int, message: String)
sealed class Response<out T : Any> : Serializable {
data class Success<out T : Any>(val data: T) : Response<T>()
data class Error(val errorText: String, val errorCode: Int) : Response<Nothing>()
}
#Cacheable(
key = "api-key",
unless = "do something here"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
In the above method, I want to cache the response only when Response.Success.data.code == (long list of codes).
Please note, in the previous line data is nothing but APIOutput object. How could I achieve it using unless or any other approach. I was thinking of writing a function that takes a doApicall method result as input and would return true or false and call that method it as unless="call a method". But I'm not sure how to do it. Any help is highly appreciated.
You can specify an expression to be evaluated in unless using SpEL. The returned value is available as result so you can do something like -
#Cacheable(
key = "api-key",
unless = "#result!=null or #result.success.data.code!=200"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
You can even use Regex in SpEL and can create custom Expression parsers if the existing functionality is not enough for your usecase.
Thanks Yatharth and John! Below is the condition that worked for me. resultcodes in the below expression is a list
#Cacheable(
key = "api-key",
unless = "!(#result instanceof T(com.abc.Response\$Success))
or (#result instanceof T(com.abc.Response\$Success)
and !(T(com.abc.APIStatus).resultCodes.contains(#result.data.code)))"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}

Recursively filter and map a list of properties

I'm using Kotlin reflection to check if attributes that have a certain annotation are null.
Given the following example:
data class DataClass(
#SomeRandomAnnotation
val otherAnnotated: String?,
val inner: InnerClass
)
data class AnotherDataClass(
#SomeRandomAnnotation
val annotatedProperty: String?,
val dataClass: DataClass
) {
fun checkCreditAnalysisConstrain() {
print(checkConstrain(this))
}
}
And the function that checks it:
fun checkConstrain(parentClass: Any): List<String> {
val filter = parentClass::class.memberProperties.filter {
if (memberIsDataClass(it)) checkConstrain(getMemberPropertyInstance(parentClass, it))
hasAnnotation(it) && propertyIsNull(it, parentClass)
}
return filter.map { formatResult(parentClass, it) }
}
The idea is that the function is going to iterate through the attributes of my classes checking if they have the annotation and checking if the value is null.
If the property is a data class, the code evaluates the properties of the childs, recursively.
After that, I map the results, transforming the KProperty's into a simple String that is human readable, containing the class name and the attribute name.
The problem is that the above code does not work as expected. The properties returned are only the properties from the first-level class.
If, instead of doing a filter, I just run a forEach and print the result, I get the expected attributes. So I'm pretty sure it's related to the recurring inside a filter.
Do you see any way of doing this in a more functional way? I'm just concerned I won't need a "temp" list and add values to the list and reset it afterwards.
Your function recursively calls itself, but does nothing with the returned list of that recursive call. That's why you only get results for the top-level class.
Also, in my opinion, you shouldn't rely on side effects happening from your filter call. It probably works, but the function's documentation does not provide a guarantee that it will be called exactly once per item in the collection. So there should be a separate for-loop to do the recursive calls, and the result should be added onto existing results.
fun checkConstrain(parent: Any): List<String> {
val memberProperties = parent::class.memberProperties
var result = memberProperties
.filter { hasAnnotation(it) && propertyIsNull(it, parent) }
.map { formatResult(parent, it) }
memberProperties.filter { memberIsDataClass(it) }
.mapNotNull { getMemberPropertyInstance(parent, it) }
.forEach { result += checkConstrain(it) }
return result
}
You didn't provide code for several of the functions you used. This is what I used for them:
val KProperty<*>.returnTypeClass get() = this.returnType.classifier as? KClass<*>
fun <T> memberIsDataClass(member: KProperty<T>) = member.returnTypeClass?.isData == true
fun <T> getMemberPropertyInstance(parent: Any, property: KProperty<T>) = property.getter.call(parent)
fun <T> hasAnnotation(property: KProperty<T>) = property.annotations.firstOrNull { it.annotationClass == SomeRandomAnnotation::class } != null
fun <T> propertyIsNull(property: KProperty<T>, parent: Any) = getMemberPropertyInstance(parent, property) == null
fun formatResult(parent: Any, property: KProperty<*>) = "$parent's property(${property.name}) is annotated with SomeRandomAnnotation and is null."

Bind ItemViewModel to domain class list

I have a Rulebook that contains Rules:
class Rulebook(val rules:MutableList<Rule>)
I have an ItemViewModel for it, as it's used in a multiply-nested selection UI.
class RulebookModel : ItemViewModel<Rulebook> {
val rulesProperty = bind // ... here's my problem
}
What is the correct binding to be able to initialize a tableview with the property?
A naive bind yields the wrong type:
val rulesProperty = bind(Rulebook::rules)
has type Property<MutableList<Rule>>, which tableview() doesn't take.
From another answer here I got Link
val rulesProperty = bind(Rulebook::rules) as ListProperty<Rule>
This yields the correct type, so we get through compilation, but at runtime I get this:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to javafx.collections.ObservableList
Note: The RulebookModel does start life without an item in it yet. I've seen ArrayLists come from empty list factory calls before. Is that possibly my actual problem?
What is the correct way to perform this binding?
Your model needs to have a SimpleListProperty to bind into an itemViewModel
Here is some sample code for how to write the classes and a table view:
data class rule(val name: String, val def: String)
class RuleBookModel{
val rulesProperty = SimpleListProperty<rule>()
var rules by rulesProperty
}
class RuleBookViewModel: ItemViewModel<RuleBookModel>() {
val rules = bind(ruleBook::rulesProperty)
}
class TestView : View("Test View") {
val myRuleBook: RuleBookViewModel by inject()
init {
// adding a rule so the table doesn't look lonely
myRuleBook.rules.value.add(rule("test", "fuga"))
}
val name = textfield()
val definition = textfield()
override val root = vbox{
hbox {
label("Name")
add(name)
}
hbox {
label("Definition")
add(definition)
}
button("Add a rule").action{
myRuleBook.rules.value.add(rule(name.text, definition.text))
}
tableview(myRuleBook.rules) {
column("name", rule::name)
column("def", rule::def)
}
}
}

Returning object of type specified in method arguments instead of AnyRef

I have the following method:
#org.springframework.stereotype.Service
class EntityCacheManager {
def get(cacheId: String, entityClass: Class[_]): AnyRef = { ... }
//...
}
So to use it, i have to write this:
val cachedEntity = entityCacheManager.get(cacheId, classOf[SomeEntity]).asInstanceOf[SomeEntity]
Is there some way to make EntityCacheManager.get() returning instance of type entityClass which is specified in method params? I'd like to avoid casting asInstanceOf every time i use this method. I know it would be nice to use generic definition of type EntityCacheManager, but it's also a spring-managed bean, so i think using generics will cause troubles.
You can use a more idiomatic scala approach by using the ClassTag typeclass
class EntityCacheManager {
def get[T: ClassTag](cacheId: String): T = {
val entityClass = implicitly[ClassTag[T]].runtimeClass
val myObject: T = ??? // you retrieve your object somehow using entityClass
myObject
}
}
you can now use it like this:
val myEntityClassInstance = get[MyEntityClass]("key")

Scala Generics - Overloaded method

Considering the given code:
val repository =
context.getBean(
Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))
and that my repositories have a String as Serializable.
I'm trying to do the following:
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))
This one works fine:
repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()
But I don't know how to put that above to work.
Assuming the method getObject(t) is retuning the correct object to be persisted and since it's a Spring Data Repository, there are 2 save method. One that accept a single entity and another for a list of entities and it says overloaded method value save.
What I have tried so far:
I saw in another thread to force the method with a type, something like this:
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t) : TYPE)
This is ok if I knew the type and also my method getObject should return that same type.
Here is my getObject method which I return the object itself without any specific type:
#throws[IOException]
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
}
So I was trying to get the type like this:
val m = Manifest.classType(getClazz(t))
type TYPE = m.type
Looks good if I force my object to this type using getObject(t) : TYPE but I don't know how to use this same type in my getObject method to be returned.
Anyway, I don't even know if this is the best approach to do this, invoking a generic repository and save a generic object.
Just to understand what I'm trying to do, I'm using a aspect to intercept a Cassandra entity to be persisted, then get it and turn into a ElasticSearch entity to save a json(thats why the getObject(t)) and replicate into ElasticSearch.
Here is the full aspect class:
#Component
#Aspect
class ElasticAop {
#Autowired val context : ApplicationContext = null
val objectMapper : ObjectMapper = new ObjectMapper()
#Pointcut("execution(* com.test.service.cassandra.*.post(..)) && args(t)")
def getPointcutPost[T](t : T) : Unit = {}
#throws[Throwable]
#Before("getPointcutPost(t)")
def elasticSaveAspect[T](joinPoint: JoinPoint, t: T) = {
val m = Manifest.classType(getClazz(t))
type TYPE = m.type
val repository =
context.getBean(
Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))
repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))
}
#throws[ClassNotFoundException]
def getClazz[T](t : T) = {
val className = t.getClass.getName.replace("cassandra", "elastic").replace("C", "E")
Class.forName(className)
}
#throws[IOException]
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
}
}
EDITED
Even setting up a type return in my getObject to Address and then setting the save method as follow save(getObject(t) : Address) give me the same overloaded error.
EDITED
I just figured out it's a limitation and a possible work around is to create a factory or something like this.
Then I created a service with a saveOrUpdate method:
trait ElasticGenericService[T <: ElasticGenericKey, R <: ElasticsearchRepository[T, String]] {
var r : R = _
def saveOrUpdate(t: T) = r.save(t)
}
and now I'm getting a cast exception:
java.lang.ClassCastException: Address cannot be cast to scala.runtime.Nothing$
What i can see here:
getObject[T](t : T) returns existential type _1 and actually kills all type checks, as you choosing the class in runtime
ElasticsearchRepository[_, String].save require existential type _2 to be passed to the save method, so _1 doesn't fit
Possible solution:
repository.asInstanceOf[ElasticsearchRepository[Any, String]].save(getObject(t).asInstanceOf[Any]) //getClass will work with runtime class instead of Any, so should be fine
Another solution (saving existential type):
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t)).asInstanceOf[T]
} //assuming T is an existential - it will return same existential as you passed

Resources