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
|
||||
|
||||
class ColumnTable(override val columns: Map<String, Column<*>>) :
|
||||
Table {
|
||||
private val rowsNum = columns.values.first().size
|
||||
/**
|
||||
* @param C bottom type for all columns in the table
|
||||
*/
|
||||
class ColumnTable<C: Any>(override val columns: Collection<Column<C>>) : Table {
|
||||
private val rowsNum = columns.first().size
|
||||
|
||||
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>
|
||||
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? {
|
||||
val value = columns[column]?.get(row)
|
||||
return when {
|
||||
value == null -> null
|
||||
type.isInstance(value) -> value as T
|
||||
else -> error("Expected type is $type, but found ${value::class}")
|
||||
return type.cast(value)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class VirtualRow(val index: Int) : Row {
|
||||
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? = getValue(index, column, type)
|
||||
}
|
||||
internal class VirtualRow(val table: Table, val index: Int) : Row {
|
||||
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? = table.getValue(index, column, type)
|
||||
}
|
||||
|
||||
class ColumnTableBuilder {
|
||||
private val columns = ArrayList<Column<*>>()
|
||||
|
||||
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 {
|
||||
inline operator fun <reified T : Any> invoke(
|
||||
name: String,
|
||||
data: List<T>,
|
||||
noinline metaBuilder: ColumnScheme.() -> Unit
|
||||
): ListColumn<T> = ListColumn(name, data, T::class, ColumnScheme(metaBuilder).toMeta())
|
||||
def: ColumnScheme,
|
||||
data: List<T?>
|
||||
): 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
|
||||
|
||||
class MapRow(val values: Map<String, Any>) : Row {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> getValue(column: String, type: KClass<out T>): T? {
|
||||
val value = values[column]
|
||||
return when {
|
||||
value == null -> null
|
||||
type.isInstance(value) -> {
|
||||
value as T?
|
||||
}
|
||||
else -> {
|
||||
error("Expected type is $type, but found ${value::class}")
|
||||
}
|
||||
}
|
||||
return type.cast(value)
|
||||
}
|
||||
}
|
@ -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 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? =
|
||||
rows[row].getValue(column, type)
|
||||
|
||||
override val columns: Map<String, Column<*>>
|
||||
get() = columnDefs.associate { it.name to VirtualColumn(it) }
|
||||
override val columns: List<Column<*>> get() = header.map { RotTableColumn(it) }
|
||||
|
||||
private inner class VirtualColumn<T : Any>(val def: ColumnDef<T>) :
|
||||
Column<T> {
|
||||
|
||||
override val name: String get() = def.name
|
||||
override val type: KClass<out T> get() = def.type
|
||||
override val meta: Meta get() = def.meta
|
||||
private inner class RotTableColumn<T : Any>(val header: ColumnHeader<T>) : Column<T> {
|
||||
override val name: String get() = header.name
|
||||
override val type: KClass<out T> get() = header.type
|
||||
override val meta: Meta get() = header.meta
|
||||
override val size: Int get() = rows.size
|
||||
|
||||
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 kotlin.reflect.KClass
|
||||
|
||||
interface Table {
|
||||
fun <T : Any> getValue(row: Int, column: String, type: KClass<out T>): T?
|
||||
val columns: Map<String, Column<*>>
|
||||
val rows: List<Row>
|
||||
//TODO to be removed in 1.3.70
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <T : Any> KClass<T>.cast(value: Any?): T? {
|
||||
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 type: KClass<out T>
|
||||
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
|
||||
operator fun get(index: Int): T?
|
||||
}
|
||||
@ -28,3 +56,6 @@ operator fun <T : Any> Column<T>.iterator() = iterator {
|
||||
interface Row {
|
||||
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