How to display data using TornadoFX treeview - treeview

I am learning how to use kotlin and have started using tornadoFX. I am going through the guide in an attempt to learn it, however I cannot figure out what is meant in the 'TreeView with Differing Types'. It seems to say that I should use star projection, which as I know it when you use a * in the call.
However as soon as I do that, the treeview says that 'Projections are not allowed on type arguments of functions and properties'
This is my code:
class MainView : View("") {
override val root = treeview<*> {
root = TreeItem(Person("Departments", ""))
cellFormat {
text = when (it) {
is String -> it
is Department -> it.name
is Person -> it.name
else -> throw IllegalArgumentException("Invalid Data Type")
}
}
populate { parent ->
val value = parent.value
if (parent == root) departments
else if (value is Department) persons.filter { it.department == value.name }
else null
} }
}
I'm honestly stumped, I don't know what I am meant to be doing.
Also if anyone else could provide me with some useful links for learning both Kotlin and tornadoFX it would be much appreciated :)

It seems the guide is actually incorrect. I got it working using treeview<Any>:
data class Department(val name: String)
data class Person(val name: String, val department: String)
val persons = listOf(
Person("Mary Hanes", "Marketing"),
Person("Steve Folley", "Customer Service"),
Person("John Ramsy", "IT Help Desk"),
Person("Erlick Foyes", "Customer Service"),
Person("Erin James", "Marketing"),
Person("Jacob Mays", "IT Help Desk"),
Person("Larry Cable", "Customer Service")
)
val departments = persons.groupBy { Department(it.department) }
override val root = treeview<Any> {
root = TreeItem("Departments")
cellFormat {
text = when (it) {
is String -> it
is Department -> it.name
is Person -> it.name
else -> kotlin.error("Invalid value type")
}
}
populate { parent ->
val value = parent.value
when {
parent == root -> departments.keys
value is Department -> departments[value]
else -> null
}
}
}

I thought i want to quit tornadofx altogether when this post saved my day. In my case i wanted to display nested lists of a object. I didn't expect something like else -> null would be necessary to prevent a stackoverlow. Somehow i ended up with this populate block which now works for me
populate { parent -> val value = parent.value
when
{
parent == root -> quotation.houses
value is NewHouse -> value.rooms
else -> null
}}

Related

Accessing Custom Options from .desc file in Protobuf

I have a proto with the following definitions.
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions{
optional bool is_key = 50002;
}
message Foo{
int64 id = 1 [(is_key) = true];
}
I generated a .desc file for the above. I was able to access all the Fields and Message defined by the FieldDescriptorProto and DescriptorProto types but not sure how to access the options defined and the value provided to it in this case is_key.
Could anyone provide me with a java version that could access the options from the .desc file
Not sure If this is the right way but all I was trying to do was read custom options from a descriptor_set.desc file.
I parsed the desc file, the problem is that it returns fileDescriptorProto Types. So I parsed them and built a dependencies graph. This helped me to create FileDescriptor Types.
Through FileDescriptor Types I was able to get Extensions and add them to extensionRegistry.
Post Which I parsed the .desc file once again but this time with extensionRegistry.
val filePathToFileDescriptorMap = mutableMapOf <String, FileDescriptor>(DescriptorProtos.getDescriptor().file.name to
DescriptorProtos.getDescriptor())
val extensionRegistry = ExtensionRegistry.newInstance()
fun buildDependencies(fileDescriptorProto: DescriptorProtos.FileDescriptorProto,
filePathToFileDescriptorProtoMap: Map<String, DescriptorProtos.FileDescriptorProto>) : FileDescriptor {
val dependencies = fileDescriptorProto.dependencyList.map { dependency ->
if(filePathToFileDescriptorMap.containsKey(dependency)) {
filePathToFileDescriptorMap[dependency]
}
else if(!filePathToFileDescriptorProtoMap.containsKey(dependency)) {
throw Exception("dependency not found $dependency")
}
else {
buildDependencies(filePathToFileDescriptorProtoMap[dependency]!!, filePathToFileDescriptorProtoMap)
}
}
filePathToFileDescriptorMap[fileDescriptorProto.name] =
FileDescriptor.buildFrom(fileDescriptorProto, dependencies.toTypedArray())
filePathToFileDescriptorMap[fileDescriptorProto.name]!!.extensions.forEach {
if(it.type == Descriptors.FieldDescriptor.Type.MESSAGE){
extensionRegistry.add(it, DynamicMessage.newBuilder(it.messageType).build())
}else{
extensionRegistry.add(it)
}
}
return filePathToFileDescriptorMap[fileDescriptorProto.name]!!
}
fun registerExtensions(){
val fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(
FileInputStream("src/generated/resources/desc_set.desc"),
)
val filePathToFileDescriptorProtoMap = fileDescriptorSet.fileList.associateBy { it.name }
fileDescriptorSet.fileList.forEach { fileDescriptorProto ->
if(!filePathToFileDescriptorMap.containsKey(fileDescriptorProto.name)){
buildDependencies(fileDescriptorProto, filePathToFileDescriptorProtoMap)
}
}
val fileDescriptorSetWithOptions = DescriptorProtos.FileDescriptorSet.parseFrom(
FileInputStream("src/generated/resources/desc_set.desc"),
extensionRegistry
)
println(fileDescriptorSetWithOptions)
}

How to place a conditional check inside springboot project reactor Mono stream (written in Kotlin)?

Pretty new to project reactor here, I am struggling to put a conditional check inside my Mono stream. This part of my application is receiving an object from Kafka. Let's say the object is like this.
data class SomeEvent(val id: String, val type: String)
I have a function that handles this object like this.
fun process(someEvent: SomeEvent): Mono<String> {
val id = someEvent.id
val checkCondition = someEvent.type == "thisType"
return repoOne.getItem(id)
.map {item ->
// WHAT DO I DO HERE TO PUT A CONDITIONAL CHECK
createEntryForItem(item)
}
.flatMap {entry ->
apiService.sendEntry(entry)
}
.flatMap {
it.bodyToMono(String::class.java)
}
.flatMap {body ->
Mono.just(body)
}
}
So, what I want to do is check whether checkCondition is true and if it is, I want to call a function repoTwo.getDetails(id) that returns a Mono<Details>.
createEntryForItem returns an object of type Entry
apiService.sendEntry(entry) returns a Mono<ClientResponse>
It'd be something like this (in my mind).
fun process(someEvent: SomeEvent): Mono<String> {
val id = someEvent.id
val checkCondition = someEvent.type == "thisType"
return repoOne.getItem(id)
.map {item ->
if (checkCondition) {
repoTwo.getDetails(id).map {details ->
createEntryForItem(item, details)
}
} else {
createEntryForItem(item)
}
}
.flatMap {entry ->
apiService.sendEntry(entry)
}
.flatMap {
it.bodyToMono(String::class.java)
}
.flatMap {body ->
Mono.just(body)
}
}
But, obviously, this does not work because the expression inside the if statement is cast to Any.
How should I write it to achieve what I want to achieve?
UPDATED: The location of where I like to have the conditional check.
You should use flatMap() and not map() after getItem().
return repoOne.getItem(id)
.flatMap {item ->
if (checkCondition) {
repoTwo.getDetails(id).map {details ->
createEntryForItem(item, details)
}
} else {
Mono.just(createEntryForItem(item))
}
}
In a map{} you can transform the value. Because you want to call getDetails() (which returns a reactive type and not a value) to do that you have to use flatMap{}. And that's why you need to wrap your item in a Mono by calling Mono.just(createEntryForItem(item)) on the else branch.
Just split it to another function. Your code will be cleaner too.
repoOne.getItem(id)
.map { createEntry(it, checkCondition) }.
.flatMap.....
private fun createEntry(item, checkCondition): Item {
return if (checkCondition) {
repoTwo.getDetails(id).map { createEntryForItem(item, it) }
} else {
createEntryForItem(item)
}
}

How to change particular property value of a class in a LiveData<List<T>> (in my case LiveData<List<Item>>) using MediatorLiveData

The Item.kt class is
#Entity(tableName = "item")
class Item(
val id: Long,
val title: String,
) {
#Ignore
var selection: Boolean = false
}
Then i make a query to get all the items in the table ,it return
LiveData<List<Item>>
Then in the viewModel i want to apply selection(true) accordig to the Mutablelivedata selectionId, the selection id contain MutableLiveData<Long> (it contain an id in the LiveData<List<Item>>)
The MyViewModel.kt code is look like this
class MyViewModel(val repository: Repository) : ViewModel() {
..........
......
val selectionId: MutableLiveData<Long> by lazy {
MutableLiveData<Long>()
}
fun setSelectionId(id: Long) {
selectionId.postValue(id)
}
..........
......
val itemLiveList: LiveData<List<Item>> = liveData(Dispatchers.IO) {
emitSource(repository.getItems())
}
}
If it is an List<Item> i can do somethig like this
val ItemWithSelection: List<Item> = repository.getItems().apply {
this.forEach {
if (it.id == selectionId) {
it.selection = true
}
}
}
but i don't know how to achieve this using Mediator LiveData . Please help me
I don't understand everything in your code, for example I have never seen a function called liveData(CoroutineDispatcher). But do you mean you want something like this?
val listWithoutSelection = liveData(Dispatchers.IO) {
emitSource(repository.getItems())
}
val listWithSelection = MediatorLiveData<List<Item>>().apply {
addSource(listWithoutSelection) { updateListSelection() }
addSource(selectionId) { updateListSelection() }
}
fun updateListSelection() {
listWithSelection.value = listWithoutSelection.value?.map {
if (it.id == selectionId.value)
it.copyWithSelection(true)
else
it
}
}
The copyWithSelection could be easily done with Kotlin data classes. It is not needed dependent on whether you want to modify the object you get from the database. If you only use that object here, you could just always reset the selection of the others to false and then you can keep the object and you don't need a copy.

fragment for flowpane in TornadoFX

The TornadoFX docs describe using the ListCellFragment to bind each cell in a list control to each item model in a list. I am wondering how to do something similar in a flowpane. I'd like to use that kind of class to render a bunch of controls and an SVG drawing in each cell. (So it would replace the button component in the example code below and somehow bind the shapeItem model to it).
class LibraryView : View("Current Library") {
val shapeLibraryViewModel : LibraryViewModel by inject()
override val root = anchorpane{
flowpane {
bindChildren(shapeLibraryViewModel.libraryItemsProperty){
shapeItem -> button(shapeItem.nameProperty)
}
}
}
}
Since I don't see a pre-made class like the one for list view, perhaps I would need to create something similar to it...or maybe there's a more lightweight approach?
Using an ItemFragment is a bit overkill, since the itemProperty of the fragment will never change (a new fragment would be created for every item whenever the libraryItemsProperty change. However, if your view logic for each item is substantial, this approach will provide a clean way to separate and contain that logic, so it might be worth it. Here is a complete example you can use as a starting point.
class ShapeItemFragment : ItemFragment<ShapeItem>() {
val shapeModel = ShapeItemModel().bindTo(this)
override val root = stackpane {
label(shapeModel.name)
}
}
class ShapeItem(name: String) {
val nameProperty = SimpleStringProperty(name)
}
class ShapeItemModel : ItemViewModel<ShapeItem>() {
val name = bind(ShapeItem::nameProperty)
}
class LibraryViewModel : ViewModel() {
val libraryItemsProperty = SimpleListProperty<ShapeItem>(
listOf(
ShapeItem("Shape 1"),
ShapeItem("Shape 2")
).observable()
)
}
class LibraryView : View("Current Library") {
val shapeLibraryViewModel: LibraryViewModel by inject()
override val root = anchorpane {
flowpane {
bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
val itemFragment = find<ShapeItemFragment>()
itemFragment.itemProperty.value = shapeItem
itemFragment.root
}
}
}
}
A slightly lighter version would be to pass the parameter into your fragment manually and just extend Fragment:
class ShapeItemFragment : Fragment() {
val item: ShapeItem by param()
override val root = stackpane {
label(item.nameProperty)
}
}
You can still bind to changes to properties inside the ShapeItem, since the underlying item won't change (as seen from the ItemFragment) anyway.
Your bindChildren statement would then look like this:
bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
find<ShapeItemFragment>(ShapeItemFragment::item to shapeItem).root
}

Scala Macro: get param default value

I have the next code, and i would like to extract the default parametr from value.
//
def extractor[T] = macro extractorImpl[T]
def extractorImpl[T: c.WeakTypeTag](c: Context) = {
//first i got a type contructor
???
}
i try with attachments but attachments.all return a Set[Any] with (for example) SymbolSourceAttachment(val name: String = "new name")
SymbolSourceAttachment contain ValDef but i do not know how to extract from SymbolSourceAttachment ValDef.
By the way i should to get a Map[String, String]("name" -> "new name")
Example:
case class Person(name: String = "new name")
object Macro {
def extractor[T] = macro extractorImpl[T]
def extractorImpl[T: c.WeakTypeTag](c: Context) = {
import c.universe._
c.weakTypeOf[T].declarations.collect {
case a: MethodSymbol if a.isConstructor =>
a.paramss.collect {
case b => b.collect {
case c =>
c.attachments.all {
case d => println(showRaw(d)) // => SymbolSourceAttachment(val name: String = "new name")
}
}
}
}
}
}
And macro should return Map("name" -> "new name")
Since you're seeing SymbolSourceAttachment, I assume you're using macro paradise (because it's an internal attachment used only in paradise), so I'll feel free to use quasiquotes :)
There's no easy way to get values of default parameters in Scala reflection API. Your best shot would be reverse-engineering the names of methods that are created to calculate default values and then referring to those.
SymbolSourceAttachment would sort of work if your macro is expanding in the same compilation run that compiles the case class, but it would break under separate compilation (attachments aren't saved in class files), and it wouldn't work in vanilla Scala (because this attachment is exclusive to paradise).
=== Macros.scala ===
import scala.reflect.macros.Context
import scala.language.experimental.macros
object Macros {
def impl[T](c: Context)(T: c.WeakTypeTag[T]): c.Expr[Map[String, Any]] = {
import c.universe._
val classSym = T.tpe.typeSymbol
val moduleSym = classSym.companionSymbol
val apply = moduleSym.typeSignature.declaration(newTermName("apply")).asMethod
// can handle only default parameters from the first parameter list
// because subsequent parameter lists might depend on previous parameters
val kvps = apply.paramss.head.map(_.asTerm).zipWithIndex.flatMap{ case (p, i) =>
if (!p.isParamWithDefault) None
else {
val getterName = newTermName("apply$default$" + (i + 1))
Some(q"${p.name.toString} -> $moduleSym.$getterName")
}
}
c.Expr[Map[String, Any]](q"Map[String, Any](..$kvps)")
}
def extractor[T]: Map[String, Any] = macro impl[T]
}
=== Test.scala ===
case class C(x: Int = 2, y: String, z: Boolean = true)(t: String = "hello")
object Test extends App {
println(Macros.extractor[C])
}
17:10 ~/Projects/Paradise2103/sandbox/src/main/scala (2.10.3)$ scalac Macros.scala && scalac Test.scala && scala Test
Map(x -> 2, z -> true)

Resources