Created
April 20, 2022 08:23
-
-
Save mskoroglu/fdff98483868e0039df8f68d7ed13469 to your computer and use it in GitHub Desktop.
Kotlin Table DSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun table(block: Table.() -> Unit): Table { | |
val table = Table() | |
table.block() | |
return table | |
} | |
class Table : Iterable<Row>, WithMetadataFilter<Row> { | |
private val rows = hashSetOf<Row>() | |
fun row(index: Int? = null, block: Row.() -> Unit): Row { | |
val row = index?.let { createRow(it) } ?: createRow() | |
row.block() | |
return row | |
} | |
fun createRow(index: Int = (getItems().lastOrNull()?.index ?: -1) + 1): Row { | |
return Row(index).also(rows::add) | |
} | |
fun <CellType> ref(rowIndex: Int, columnIndex: Int): Cell<CellType> { | |
@Suppress("UNCHECKED_CAST") | |
return rows | |
.find { it.index == rowIndex }!! | |
.find { it.columnIndex == columnIndex }!! as Cell<CellType> | |
} | |
fun <CellType> ref(row: Row, columnIndex: Int): Cell<CellType> { | |
return ref(row.index, columnIndex) | |
} | |
override fun getItems(): Set<Row> { | |
return rows.sortedBy(Row::index).toSet() | |
} | |
override fun iterator(): Iterator<Row> { | |
return getItems().iterator() | |
} | |
override fun hashCode(): Int { | |
return getItems().hashCode() | |
} | |
override fun equals(other: Any?): Boolean { | |
return other is Table && getItems() == other.getItems() | |
} | |
} | |
class Row(val index: Int) : Iterable<Cell<*>>, WithMetadataFilter<Cell<*>>, WithMetadata<Row>() { | |
private val cells = hashSetOf<Cell<*>>() | |
private fun <CellType> createCell(): Cell<CellType> { | |
return Cell<CellType>(rowIndex = index, columnIndex = cells.size).also(cells::add) | |
} | |
fun <CellType> cell(block: Cell<*>.() -> CellType): Cell<CellType> { | |
val cell = createCell<CellType>() | |
val value = cell.block() | |
cell.setValue { value } | |
return cell | |
} | |
fun <CellType> createCell(value: CellType): Cell<CellType> { | |
return createCell<CellType>().also { it.setValue { value } } | |
} | |
fun <CellType> ref(columnIndex: Int): Cell<CellType> { | |
@Suppress("UNCHECKED_CAST") | |
return find { it.columnIndex == columnIndex }!! as Cell<CellType> | |
} | |
override fun getItems(): Set<Cell<*>> { | |
return cells.sortedBy(Cell<*>::columnIndex).toSet() | |
} | |
override fun iterator(): Iterator<Cell<*>> { | |
return getItems().iterator() | |
} | |
override fun hashCode(): Int { | |
return getItems().hashCode() | |
} | |
override fun equals(other: Any?): Boolean { | |
return other is Row && getItems() == other.getItems() | |
} | |
} | |
class Cell<CellType>( | |
val rowIndex: Int, | |
val columnIndex: Int | |
) : WithMetadata<Cell<CellType>>() { | |
private var isCalculated = false | |
private var calculatedValue: CellType? = null | |
private var valueSupplier: (Cell<CellType>.() -> CellType)? = null | |
internal fun setValue(valueSupplier: Cell<CellType>.() -> CellType) { | |
this.valueSupplier = valueSupplier | |
} | |
val value: CellType? | |
get() { | |
if (!this.isCalculated) { | |
calculatedValue = valueSupplier!!(this) | |
isCalculated = true | |
} | |
return calculatedValue | |
} | |
fun getRef(): String { | |
return "${rowIndex}:${columnIndex}" | |
} | |
override fun hashCode(): Int { | |
return getRef().hashCode() | |
} | |
override fun equals(other: Any?): Boolean { | |
return other is Cell<*> && getRef() == other.getRef() | |
} | |
} | |
open class WithMetadata<RowOrCell> { | |
private var metadata: MutableMap<String, Any?>? = null | |
fun getMetadata(): Map<String, Any?>? { | |
return metadata?.toMap() | |
} | |
fun <MetadataValue> metadata(key: String): MetadataValue? { | |
@Suppress("UNCHECKED_CAST") | |
return metadata?.get(key) as? MetadataValue | |
} | |
fun <MetadataValue> metadata(key: String, value: MetadataValue): RowOrCell { | |
if (metadata == null) { | |
metadata = mutableMapOf() | |
} | |
metadata!![key] = value | |
@Suppress("UNCHECKED_CAST") | |
return this as RowOrCell | |
} | |
} | |
interface WithMetadataFilter<ItemType : WithMetadata<*>> { | |
fun getItems(): Set<ItemType> | |
fun <MetadataValue> filter(key: String, value: Any?): Set<ItemType> { | |
return getItems().filter { it.metadata<MetadataValue>(key) == value }.toSet() | |
} | |
fun <MetadataValue> find(key: String, value: Any?): ItemType? { | |
return getItems().find { it.metadata<MetadataValue>(key) == value } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment