API for tables
This commit is contained in:
parent
819ab3ab20
commit
061c570407
@ -0,0 +1,10 @@
|
|||||||
|
package hep.dataforge.tables
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
data class ColumnDef<out T : Any>(
|
||||||
|
override val name: String,
|
||||||
|
override val type: KClass<out T>,
|
||||||
|
override val meta: Meta
|
||||||
|
): ColumnHeader<T>
|
@ -2,34 +2,26 @@ package hep.dataforge.tables
|
|||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class ColumnTable(override val columns: Map<String, Column<*>>) :
|
/**
|
||||||
Table {
|
* @param C bottom type for all columns in the table
|
||||||
private val rowsNum = columns.values.first().size
|
*/
|
||||||
|
class ColumnTable<C: Any>(override val columns: Collection<Column<C>>) : Table {
|
||||||
|
private val rowsNum = columns.first().size
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(columns.values.all { it.size == rowsNum }) { "All columns must be of the same size" }
|
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val rows: List<Row>
|
override val rows: List<Row>
|
||||||
get() = (0 until rowsNum).map { VirtualRow(it) }
|
get() = (0 until rowsNum).map { VirtualRow(this, it) }
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
override fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
||||||
val value = columns[column]?.get(row)
|
val value = columns[column]?.get(row)
|
||||||
return when {
|
return type.cast(value)
|
||||||
value == null -> null
|
|
||||||
type.isInstance(value) -> value as T
|
|
||||||
else -> error("Expected type is $type, but found ${value::class}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class VirtualRow(val index: Int) : Row {
|
|
||||||
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? = getValue(index, column, type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ColumnTableBuilder {
|
internal class VirtualRow(val table: Table, val index: Int) : Row {
|
||||||
private val columns = ArrayList<Column<*>>()
|
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? = table.getValue(index, column, type)
|
||||||
|
}
|
||||||
|
|
||||||
fun build() = ColumnTable(columns.associateBy { it.name })
|
|
||||||
}
|
|
@ -0,0 +1,58 @@
|
|||||||
|
package hep.dataforge.tables
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class ColumnTableBuilder(val size: Int) : Table {
|
||||||
|
private val _columns = ArrayList<Column<*>>()
|
||||||
|
|
||||||
|
override val columns: List<Column<*>> get() = _columns
|
||||||
|
override val rows: List<Row> get() = (0 until size).map {
|
||||||
|
VirtualRow(this, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
||||||
|
val value = columns[column]?.get(row)
|
||||||
|
return type.cast(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fixed column to the end of the table
|
||||||
|
*/
|
||||||
|
fun add(column: Column<*>) {
|
||||||
|
require(column.size == this.size) { "Required column size $size, but found ${column.size}" }
|
||||||
|
_columns.add(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a column at [index]
|
||||||
|
*/
|
||||||
|
fun insert(index: Int, column: Column<*>) {
|
||||||
|
require(column.size == this.size) { "Required column size $size, but found ${column.size}" }
|
||||||
|
_columns.add(index, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapColumn<T : Any, R : Any>(
|
||||||
|
val source: Column<T>,
|
||||||
|
override val type: KClass<out R>,
|
||||||
|
override val name: String,
|
||||||
|
override val meta: Meta = source.meta,
|
||||||
|
val mapper: (T?) -> R?
|
||||||
|
) : Column<R> {
|
||||||
|
override val size: Int get() = source.size
|
||||||
|
|
||||||
|
override fun get(index: Int): R? = mapper(source[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
class CachedMapColumn<T : Any, R : Any>(
|
||||||
|
val source: Column<T>,
|
||||||
|
override val type: KClass<out R>,
|
||||||
|
override val name: String,
|
||||||
|
override val meta: Meta = source.meta,
|
||||||
|
val mapper: (T?) -> R?
|
||||||
|
) : Column<R> {
|
||||||
|
override val size: Int get() = source.size
|
||||||
|
private val values: HashMap<Int, R?> = HashMap()
|
||||||
|
override fun get(index: Int): R? = values.getOrPut(index) { mapper(source[index]) }
|
||||||
|
}
|
@ -16,9 +16,16 @@ class ListColumn<T : Any>(
|
|||||||
companion object {
|
companion object {
|
||||||
inline operator fun <reified T : Any> invoke(
|
inline operator fun <reified T : Any> invoke(
|
||||||
name: String,
|
name: String,
|
||||||
data: List<T>,
|
def: ColumnScheme,
|
||||||
noinline metaBuilder: ColumnScheme.() -> Unit
|
data: List<T?>
|
||||||
): ListColumn<T> = ListColumn(name, data, T::class, ColumnScheme(metaBuilder).toMeta())
|
): ListColumn<T> = ListColumn(name, data, T::class, def.toMeta())
|
||||||
|
|
||||||
|
inline operator fun <reified T : Any> invoke(
|
||||||
|
name: String,
|
||||||
|
def: ColumnScheme,
|
||||||
|
size: Int,
|
||||||
|
dataBuilder: (Int) -> T?
|
||||||
|
): ListColumn<T> = invoke(name, def, List(size, dataBuilder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,17 +3,8 @@ package hep.dataforge.tables
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class MapRow(val values: Map<String, Any>) : Row {
|
class MapRow(val values: Map<String, Any>) : Row {
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? {
|
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? {
|
||||||
val value = values[column]
|
val value = values[column]
|
||||||
return when {
|
return type.cast(value)
|
||||||
value == null -> null
|
|
||||||
type.isInstance(value) -> {
|
|
||||||
value as T?
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
error("Expected type is $type, but found ${value::class}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package hep.dataforge.tables
|
||||||
|
|
||||||
|
import hep.dataforge.meta.scheme.Scheme
|
||||||
|
import hep.dataforge.meta.scheme.SchemeSpec
|
||||||
|
import hep.dataforge.meta.scheme.string
|
||||||
|
|
||||||
|
class MetaQuery : Scheme() {
|
||||||
|
var field by string()
|
||||||
|
|
||||||
|
companion object : SchemeSpec<MetaQuery>(::MetaQuery)
|
||||||
|
}
|
@ -3,24 +3,21 @@ package hep.dataforge.tables
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
data class ColumnDef<T : Any>(val name: String, val type: KClass<T>, val meta: Meta)
|
|
||||||
|
|
||||||
class RowTable<R : Row>(override val rows: List<R>, private val columnDefs: List<ColumnDef<*>>) : Table {
|
class RowTable<R : Row>(override val rows: List<R>, override val header: TableHeader) : Table {
|
||||||
override fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T? =
|
override fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T? =
|
||||||
rows[row].getValue(column, type)
|
rows[row].getValue(column, type)
|
||||||
|
|
||||||
override val columns: Map<String, Column<*>>
|
override val columns: List<Column<*>> get() = header.map { RotTableColumn(it) }
|
||||||
get() = columnDefs.associate { it.name to VirtualColumn(it) }
|
|
||||||
|
|
||||||
private inner class VirtualColumn<T : Any>(val def: ColumnDef<T>) :
|
private inner class RotTableColumn<T : Any>(val header: ColumnHeader<T>) : Column<T> {
|
||||||
Column<T> {
|
override val name: String get() = header.name
|
||||||
|
override val type: KClass<out T> get() = header.type
|
||||||
override val name: String get() = def.name
|
override val meta: Meta get() = header.meta
|
||||||
override val type: KClass<out T> get() = def.type
|
|
||||||
override val meta: Meta get() = def.meta
|
|
||||||
override val size: Int get() = rows.size
|
override val size: Int get() = rows.size
|
||||||
|
|
||||||
override fun get(index: Int): T? = rows[index].getValue(name, type)
|
override fun get(index: Int): T? = rows[index].getValue(name, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,16 +3,44 @@ package hep.dataforge.tables
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
interface Table {
|
//TODO to be removed in 1.3.70
|
||||||
fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T?
|
@Suppress("UNCHECKED_CAST")
|
||||||
val columns: Map<String, Column<*>>
|
internal fun <T : Any> KClass<T>.cast(value: Any?): T? {
|
||||||
val rows: List<Row>
|
return when {
|
||||||
|
value == null -> null
|
||||||
|
!isInstance(value) -> error("Expected type is $this, but found ${value::class}")
|
||||||
|
else -> value as T
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Column<out T : Any> {
|
typealias TableHeader = List<ColumnHeader<*>>
|
||||||
|
|
||||||
|
|
||||||
|
interface Table {
|
||||||
|
fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T?
|
||||||
|
val columns: Collection<Column<*>>
|
||||||
|
val header: TableHeader get() = columns.toList()
|
||||||
|
val rows: List<Row>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply query to a table and return lazy Flow
|
||||||
|
*/
|
||||||
|
//fun find(query: Any): Flow<Row>
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Collection<Column<*>>.get(name: String): Column<*>? = find { it.name == name }
|
||||||
|
|
||||||
|
inline operator fun <reified T : Any> Table.get(row: Int, column: String): T? = getValue(row, column, T::class)
|
||||||
|
|
||||||
|
interface ColumnHeader<out T : Any> {
|
||||||
val name: String
|
val name: String
|
||||||
val type: KClass<out T>
|
val type: KClass<out T>
|
||||||
val meta: Meta
|
val meta: Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T : Any> Table.get(row: Int, column: Column<T>): T? = getValue(row, column.name, column.type)
|
||||||
|
|
||||||
|
interface Column<out T : Any> : ColumnHeader<T> {
|
||||||
val size: Int
|
val size: Int
|
||||||
operator fun get(index: Int): T?
|
operator fun get(index: Int): T?
|
||||||
}
|
}
|
||||||
@ -20,11 +48,14 @@ interface Column<out T : Any> {
|
|||||||
val Column<*>.indices get() = (0 until size)
|
val Column<*>.indices get() = (0 until size)
|
||||||
|
|
||||||
operator fun <T : Any> Column<T>.iterator() = iterator {
|
operator fun <T : Any> Column<T>.iterator() = iterator {
|
||||||
for (i in indices){
|
for (i in indices) {
|
||||||
yield(get(i))
|
yield(get(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Row {
|
interface Row {
|
||||||
fun <T : Any> getValue(column: String, type: KClass<out T>): T?
|
fun <T : Any> getValue(column: String, type: KClass<out T>): T?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline operator fun <reified T : Any> Row.get(column: String): T? = getValue(column, T::class)
|
||||||
|
operator fun <T : Any> Row.get(column: Column<T>): T? = getValue(column.name, column.type)
|
@ -0,0 +1,36 @@
|
|||||||
|
package hep.dataforge.tables
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.reflect.full.cast
|
||||||
|
import kotlin.reflect.full.isSubclassOf
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : Any> Column<*>.cast(type: KClass<T>): Column<T> {
|
||||||
|
return if (type.isSubclassOf(this.type)) {
|
||||||
|
this as Column<T>
|
||||||
|
} else {
|
||||||
|
CastColumn(this, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CastColumn<T : Any>(val origin: Column<*>, override val type: KClass<T>) : Column<T> {
|
||||||
|
override val name: String get() = origin.name
|
||||||
|
override val meta: Meta get() = origin.meta
|
||||||
|
override val size: Int get() = origin.size
|
||||||
|
|
||||||
|
|
||||||
|
override fun get(index: Int): T? = type.cast(origin[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColumnProperty<T : Any>(val table: Table, val type: KClass<T>) : ReadOnlyProperty<Any?, Column<T>> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Column<T> {
|
||||||
|
val name = property.name
|
||||||
|
return (table.columns[name] ?: error("Column with name $name not found in the table")).cast(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T : Any> Collection<Column<*>>.get(header: ColumnHeader<T>): Column<T>? =
|
||||||
|
find { it.name == header.name }?.cast(header.type)
|
@ -1,37 +0,0 @@
|
|||||||
package hep.dataforge.tables
|
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.reflect.full.cast
|
|
||||||
import kotlin.reflect.full.isSubclassOf
|
|
||||||
|
|
||||||
class TableAccessor(val table: Table) : Table by table {
|
|
||||||
inline fun <reified T : Any> column() = ColumnProperty(table, T::class)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T : Any> Column<*>.cast(type: KClass<T>): Column<T> {
|
|
||||||
return if (type.isSubclassOf(this.type)) {
|
|
||||||
this as Column<T>
|
|
||||||
} else {
|
|
||||||
ColumnWrapper(this, type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ColumnWrapper<T : Any>(val column: Column<*>, override val type: KClass<T>) : Column<T> {
|
|
||||||
override val name: String get() = column.name
|
|
||||||
override val meta: Meta get() = column.meta
|
|
||||||
override val size: Int get() = column.size
|
|
||||||
|
|
||||||
|
|
||||||
override fun get(index: Int): T? = type.cast(column[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
class ColumnProperty<T : Any>(val table: Table, val type: KClass<T>) : ReadOnlyProperty<Any?, Column<T>?> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Column<T>? {
|
|
||||||
val name = property.name
|
|
||||||
return table.columns[name]?.cast(type)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user