Kotlin and TornadoFX

Posted on October 19, 2016

Converter for the old validation configuration written in Kotlin.

As I have changed the JSON for configure the SmartCSV.fx from my own Schema to JSON Table Schema, I had to provide a converter for existing configurations.

I wanted to write less code than I typically do in Java and learn something new. Therefor I started a new project in Kotlin a programming language invented by JetBrains, the people behind the excellent IntelliJ IDEA IDE.

To make my life even easier, I use klaxon a JSON parser for Kotlin, which allows a very simple way to access JSON artefacts.

1
2
3
{
  "headers": { "list": ["COLUMN 1","COLUMN 2","..."] }
}

 

1
2
3
4
5
val headers = root.obj("headers")
val headerList: JsonArray<String>? = headers?.array("list")
headerList?.forEach {
  // process the columns
}

As you can see, it is pretty straight forward to use klaxon. I just need the ? as the value may be null.

1
2
3
4
5
6
{
  "columns": {
    "COLUMN 1": { ... },
    "COLUMN 2": { ... },
    "...": { ... }.
}

 

1
2
3
4
5
6
7
val columnMap = root.obj("columns")
headerList?.forEach {
  val column: JsonObject? = columnMap?.obj(it)
  if (column != null) {
    // process existing column definition
  }
}

In my JSON config file, not all columns had to be defined. Only the ones with validation rules. That is the reason, why not each column has to be in the map and the column may be null.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
val isInteger = column.boolean("integer") ?: false
val isNotEmpty = column.boolean("not empty") ?: false
val isDouble = column.boolean("double") ?: false
val isUnique = column.boolean("unique") ?: false
val isAlphanumeric = column.boolean("alphanumeric") ?: false
val minlength = column.int("minlength")
val maxlength = column.int("maxlength")
val date = column.string("date")
val groovy = column.string("groovy")
val regexp = column.string("regexp")
val valueOfList: JsonArray<String>? = column.array("value of")

When the column exist, just grab all possible values. In case of the boolean value I am using a kotlin construct ?: to provide a default value if the method returns null.

To step further into the code, just have a look at Converter.kt

TornadoFX

I wanted a simple JavaFX UI for the converter, as a command line call is not as comfortable as a button click. TornadoFX is a lightweight JavaFX framework for Kotlin, which allows you to define a JavaFX app in a few lines. Edvin Syse has done a great job, by providing an easy DSL to define a JavaFX UI.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
with(root) {
	textfield() {
		isEditable = false
		isDisable = false
		isMouseTransparent = true
		isFocusTraversable = false
		useMaxWidth = true
		bind(fileinput.nameProperty())
		gridpaneConstraints {
			columnRowIndex(0, 0)
			hGrow = Priority.ALWAYS
		}
	}

	button("Select File") {
		gridpaneConstraints { columnRowIndex(1, 0) }
		setOnAction {
			state.text = null
			val selectedFile = fileChooser.showOpenDialog(primaryStage)
			if (selectedFile != null) {
				fileinput.name = selectedFile.absolutePath
			}
		}
	}

	button("Convert") {
		gridpaneConstraints { columnRowIndex(1, 1) }
		setOnAction {
			controller.convert(fileinput.name)
			state.text = "converted ${fileinput.name}"
			fileinput.name = null
		}
		disableProperty().bind(fileinput.nameProperty().isNull)
	}
}

This is more or less the complete definition of the UI including action handling for the buttons. While reading the code, you know exactly what this will look like!

SmartCSV.fx.Converter UI

For the complete source code, have a look at ConverterFX.kt. And that’s all. Two small Kotlin files! One for converting the JSON and one for the UI. Had so much fun and I am sure it could be done a lot better than I did!